Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
emergency-project
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
吴春元
emergency-project
Commits
ff20fbee
Commit
ff20fbee
authored
Feb 12, 2025
by
York
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
初版完成
parent
e9938e34
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
344 additions
and
319 deletions
+344
-319
home.js
src/api/home/home.js
+8
-8
wm.js
src/api/wm/wm.js
+7
-8
wm.vue
src/layout/wm.vue
+15
-11
index.ts
src/router/index.ts
+2
-2
config.js
src/utils/config.js
+2
-4
HomeView.vue
src/views/home/home/HomeView.vue
+22
-14
WmHome.vue
src/views/wm/home/WmHome.vue
+19
-9
IMessage.vue
src/views/wm/imessage/IMessage.vue
+133
-94
MessageDetail.vue
src/views/wm/message/MessageDetail.vue
+28
-32
MessageList.vue
src/views/wm/message/MessageList.vue
+90
-80
RecoverList.vue
src/views/wm/recover/RecoverList.vue
+18
-57
No files found.
src/api/home/home.js
View file @
ff20fbee
import
request
from
"@/utils/request"
;
// 轮播图
export
function
getBanner
(
query
)
{
return
request
({
url
:
"/exam/setup/list"
,
method
:
"get"
,
params
:
query
,
});
}
// http://192.168.19.142:9002/cms/category/list?categoryFlag=tyshlwyjzhhjbzx
export
function
getTab
(
data
)
{
return
request
({
...
...
@@ -16,6 +8,14 @@ export function getTab(data) {
params
:
data
,
});
}
//获取友情链接
export
function
getLink
(
data
)
{
return
request
({
url
:
"/cms/websitelink/list"
,
method
:
"post"
,
params
:
data
,
});
}
// // 查询考试设置详细
// export function getSetup(id) {
...
...
src/api/wm/wm.js
View file @
ff20fbee
...
...
@@ -3,8 +3,8 @@ import request from "@/utils/request";
// 热点留言列表
export
function
getHotList
(
data
)
{
return
request
({
url
:
"/cms/
content
/list"
,
method
:
"
ge
t"
,
url
:
"/cms/
leavemessage
/list"
,
method
:
"
pos
t"
,
data
:
data
,
});
}
...
...
@@ -12,8 +12,8 @@ export function getHotList(data) {
// 最新回复列表
export
function
getRecoverList
(
data
)
{
return
request
({
url
:
"/cms/
content/l
ist"
,
method
:
"
ge
t"
,
url
:
"/cms/
leavemessage/replyL
ist"
,
method
:
"
pos
t"
,
data
:
data
,
});
}
...
...
@@ -21,18 +21,17 @@ export function getRecoverList(data) {
// 我要留言
export
function
getWantMessage
(
data
)
{
return
request
({
url
:
"/cms/
content/list
"
,
url
:
"/cms/
leavemessage/saveEntity
"
,
method
:
"post"
,
data
:
data
,
});
}
// 留言详情
export
function
getMessageDetail
(
data
)
{
export
function
getMessageDetail
(
id
)
{
return
request
({
url
:
"/cms/
content/list"
,
url
:
"/cms/
leavemessage/get?id="
+
id
,
method
:
"get"
,
data
:
data
,
});
}
...
...
src/layout/wm.vue
View file @
ff20fbee
...
...
@@ -156,17 +156,21 @@ function iMessageClick() {
}
function
handleSearch
()
{
if
(
searchQuery
.
value
)
{
router
.
push
({
path
:
"/wm/search"
,
query
:
{
searchQuery
:
searchQuery
.
value
},
});
}
else
{
ElMessage
({
message
:
"请输入搜索内容"
,
type
:
"warning"
,
});
}
router
.
push
({
path
:
"/wm/messagelist"
,
query
:
{
keywords
:
searchQuery
.
value
},
});
// if (searchQuery.value) {
// router.push({
// path: "/wm/search",
// query: { searchQuery: searchQuery.value },
// });
// } else {
// ElMessage({
// message: "请输入搜索内容",
// type: "warning",
// });
// }
}
</
script
>
<
style
scoped
>
...
...
src/router/index.ts
View file @
ff20fbee
import
{
createRouter
,
createWebHistory
}
from
"vue-router"
;
import
{
createRouter
,
createWebH
ashH
istory
}
from
"vue-router"
;
// 首页新闻
import
NewsLayout
from
"../layout/news.vue"
;
...
...
@@ -32,7 +32,7 @@ import IMessage from "../views/wm/imessage/IMessage.vue";
import
WmSearchList
from
"../views/wm/search/WmSearchList.vue"
;
const
router
=
createRouter
({
history
:
createWebHistory
(
import
.
meta
.
env
.
BASE_URL
),
history
:
createWebH
ashH
istory
(
import
.
meta
.
env
.
BASE_URL
),
routes
:
[
{
path
:
"/"
,
...
...
src/utils/config.js
View file @
ff20fbee
let
baseImageUrl
;
if
(
process
.
env
.
NODE_ENV
===
"development"
)
{
// baseImageUrl = (path) => `${window.location.origin}${path}`;
baseImageUrl
=
(
path
)
=>
"http://192.168.19.142:9002"
+
path
;
// console.log(baseImageUrl);
}
else
{
baseImageUrl
=
(
path
)
=>
path
;
baseImageUrl
=
(
path
)
=>
"http://yjzh.sxyztech.cn"
+
path
;
}
export
{
baseImageUrl
};
export
{
baseImageUrl
};
// 栏目属性
export
const
HLW_YJZH_JBZX
=
"tyshlwyjzhhjbzx"
;
//太原市互联网应急指挥和举报中心
export
const
HLW_LH_PYPT
=
"sxhlwlhpypt"
;
//山西互联网联合辟谣平台
...
...
src/views/home/home/HomeView.vue
View file @
ff20fbee
...
...
@@ -264,7 +264,7 @@
<a
v-for=
"(link, index) in friendLinks"
:key=
"index"
:href=
"link.
u
rl"
:href=
"link.
webLinkU
rl"
target=
"_blank"
class=
"text-gray-600 hover:text-blue-600 text-center"
>
...
...
@@ -292,8 +292,9 @@ import "swiper/css";
import
"swiper/css/pagination"
;
import
"swiper/css/autoplay"
;
import
"swiper/css/navigation"
;
import
{
get
Banner
}
from
"@/api/home/home"
;
import
{
get
Link
}
from
"@/api/home/home"
;
import
{
getNewsList
}
from
"@/api/home/news/list"
;
import
{
baseImageUrl
,
rdxw
,
...
...
@@ -329,10 +330,6 @@ const tyNews = ref([]);
const
hotNews
=
ref
([]);
const
authoritativeNews
=
ref
([]);
const
rumourNews
=
ref
([]);
//获取轮播图数据
getBanner
().
then
((
response
)
=>
{
console
.
log
(
response
);
});
getTyNewsList
(
5
,
1
);
...
...
@@ -481,6 +478,17 @@ function getLbtList(pageSize: number, pageNo: number) {
});
}
getLinkLists
();
//获取首页轮播图
function
getLinkLists
()
{
getLink
().
then
((
response
)
=>
{
const
data
=
response
.
data
;
const
rowsList
=
data
.
rows
;
friendLinks
.
value
=
rowsList
;
});
}
const
friendLinks
=
ref
([]);
const
slides
=
ref
([]);
const
quickEntries
=
[
...
...
@@ -514,14 +522,14 @@ const quickEntries = [
// },
// ];
const
friendLinks
=
[
{
name
:
"太原市人民政府"
,
url
:
"https://www.taiyuan.gov.cn"
},
{
name
:
"山西省网信办"
,
url
:
"http://www.casx.gov.cn"
},
{
name
:
"太原日报"
,
url
:
"http://www.tynews.com.cn"
},
{
name
:
"山西新闻网"
,
url
:
"https://www.sxgov.cn"
},
{
name
:
"太原广播电视台"
,
url
:
"https://www.sxtygdy.com"
},
{
name
:
"太原市公安局"
,
url
:
"https://gaj.taiyuan.gov.cn"
},
];
//
const friendLinks = [
//
{ name: "太原市人民政府", url: "https://www.taiyuan.gov.cn" },
//
{ name: "山西省网信办", url: "http://www.casx.gov.cn" },
//
{ name: "太原日报", url: "http://www.tynews.com.cn" },
//
{ name: "山西新闻网", url: "https://www.sxgov.cn" },
//
{ name: "太原广播电视台", url: "https://www.sxtygdy.com" },
//
{ name: "太原市公安局", url: "https://gaj.taiyuan.gov.cn" },
//
];
</
script
>
<
style
scoped
>
.swiper
{
...
...
src/views/wm/home/WmHome.vue
View file @
ff20fbee
...
...
@@ -17,20 +17,25 @@
</div>
<div
class=
"space-y-3"
>
<div
v-for=
"(item, index) in hot
Topics
"
v-for=
"(item, index) in hot
List
"
:key=
"index"
class=
"p-4 bg-white rounded-lg hover:shadow-md transition-shadow border-l-4 border-red-500"
@
click=
"messageClick(item)"
@
click=
"messageClick(item
.id
)"
>
<div
class=
"flex items-center justify-between"
>
<span
class=
"text-gray-400 text-sm"
>
{{
item
.
date
}}
</span>
<div
class=
"flex items-center text-sm text-gray-500"
>
<span>
{{
item
.
contentDatetime
}}
</span>
<span
class=
"mx-5"
>
留言人:
{{
item
.
name
}}
</span>
</div>
<el-tag
size=
"small"
:type=
"item.
status === '已回复
' ? 'success' : 'warning'"
:type=
"item.
replyStatus === '1
' ? 'success' : 'warning'"
>
{{
item
.
status
}}
{{
item
.
replyStatus
===
"1"
?
"已回复"
:
"未回复"
}}
</el-tag>
</div>
<h4
class=
"text-gray-900 font-medium mt-3"
>
{{
item
.
title
}}
</h4>
<p
class=
"mt-2 text-gray-700"
>
{{
item
.
content
}}
</p>
</div>
</div>
...
...
@@ -50,7 +55,7 @@
</div>
<div
class=
"space-y-3"
>
<div
v-for=
"(item, index) in re
plies
"
v-for=
"(item, index) in re
coverList
"
:key=
"index"
class=
"p-4 bg-white rounded-lg hover:shadow-md transition-shadow border-l-4 border-blue-500"
@
click=
"recoverClick(item.id)"
...
...
@@ -59,7 +64,13 @@
<div
class=
"flex-1"
>
<div
class=
"flex items-center justify-between"
>
<h4
class=
"text-gray-900 font-medium"
>
{{
item
.
title
}}
</h4>
<span
class=
"text-gray-500 text-sm"
>
{{
item
.
date
}}
</span>
<!--
<span
class=
"text-gray-500 text-sm"
>
{{
item
.
contentDatetime
}}
</span>
-->
<div
class=
"flex items-center text-sm text-gray-500"
>
<span>
{{
item
.
contentDatetime
}}
</span>
<span
class=
"mx-5"
>
留言人:
{{
item
.
name
}}
</span>
</div>
</div>
</div>
</div>
...
...
@@ -163,7 +174,6 @@ const replies = ref([
// path: "/wm/messagelist",
// });
// }
const
hotList
=
ref
([]);
const
recoverList
=
ref
([]);
...
...
@@ -207,7 +217,7 @@ function messageClick(id: number) {
}
function
recoverClick
(
id
:
number
)
{
router
.
push
({
path
:
"/wm/
recover
detail"
,
path
:
"/wm/
message
detail"
,
query
:
{
pathType
:
"-1"
,
id
:
id
,
...
...
src/views/wm/imessage/IMessage.vue
View file @
ff20fbee
...
...
@@ -73,6 +73,7 @@
<div
class=
"col-span-8"
>
<div
class=
"bg-white rounded-lg shadow-sm p-8"
>
<h2
class=
"text-2xl font-bold text-gray-800 mb-6"
>
提交留言
</h2>
=
<form
@
submit
.
prevent=
"handleSubmit"
class=
"space-y-6"
>
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
...
...
@@ -86,51 +87,51 @@
</div>
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
<span
class=
"text-red-500"
>
*
</span>
留言标题
<span
class=
"text-red-500"
>
*
</span>
联系电话
</label>
<el-input
v-model=
"formData.
title
"
placeholder=
"请输入
留言标题
"
v-model=
"formData.
phoneNumber
"
placeholder=
"请输入
联系电话
"
class=
"!rounded-button"
/>
</div>
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
<span
class=
"text-red-500"
>
*
</span>
详细内容
<span
class=
"text-red-500"
>
*
</span>
留言标题
</label>
<el-input
v-model=
"formData.content"
type=
"textarea"
:rows=
"6"
placeholder=
"请输入详细内容"
v-model=
"formData.title"
placeholder=
"请输入留言标题"
class=
"!rounded-button"
/>
</div>
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
<span
class=
"text-red-500"
>
*
</span>
联系电话
<span
class=
"text-red-500"
>
*
</span>
留言内容
</label>
<el-input
v-model=
"formData.phone"
placeholder=
"请输入联系电话"
v-model=
"formData.content"
type=
"textarea"
:rows=
"6"
placeholder=
"请输入留言内容"
class=
"!rounded-button"
/>
</div>
<div
class=
"space-y-2"
>
<!--
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
上传附件
</label>
<el-upload
:file-list=
"fileList"
class=
"upload-demo"
action=
"
https://jsonplaceholder.typicode.com/posts/
"
action=
"
/ms/file/upload.do
"
:before-remove=
"beforeRemove"
multiple
:limit=
"3"
ref=
"uploadRef"
:on-success=
"handleSuccess"
:on-exceed=
"handleExceed"
:file-list=
"fileList"
:auto-upload=
"true"
accept=
".jpg,.png,.doc,.docx,.xslx,pdf"
>
<el-button
...
...
@@ -144,7 +145,7 @@
</div>
</
template
>
</el-upload>
</div>
</div>
-->
<div
class=
"space-y-2"
>
<label
class=
"block text-sm font-medium text-gray-700"
>
<span
class=
"text-red-500"
>
*
</span>
验证码
...
...
@@ -190,6 +191,8 @@ import { Warning, ArrowRight, Message } from "@element-plus/icons-vue";
import
BreadCrumb
from
"@/components/breadcrumb/BreadCrumb.vue"
;
import
{
getWantMessage
,
uploadFile
}
from
"@/api/wm/wm"
;
import
{
ElMessageBox
}
from
"element-plus"
;
import
{
baseImageUrl
}
from
"@/utils/config"
;
// const { proxy } = getCurrentInstance();
const
breadcrumbItems
=
ref
([
...
...
@@ -197,69 +200,18 @@ const breadcrumbItems = ref([
{
title
:
"我要留言"
},
]);
const
letters
=
Array
.
from
(
"ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
);
const
generateCaptcha
=
()
=>
{
const
canvas
=
document
.
createElement
(
"canvas"
);
const
ctx
=
canvas
.
getContext
(
"2d"
);
if
(
!
ctx
)
return
""
;
canvas
.
width
=
128
;
canvas
.
height
=
40
;
// 设置背景
ctx
.
fillStyle
=
"#fff"
;
ctx
.
fillRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
// 生成验证码文字
const
captchaText
=
Array
.
from
(
{
length
:
4
},
()
=>
letters
[
Math
.
floor
(
Math
.
random
()
*
letters
.
length
)]
).
join
(
""
);
// 绘制文字
ctx
.
font
=
"bold 24px Arial"
;
ctx
.
textBaseline
=
"middle"
;
// 随机颜色和位置绘制每个字符
for
(
let
i
=
0
;
i
<
captchaText
.
length
;
i
++
)
{
const
x
=
20
+
i
*
25
;
const
y
=
20
+
Math
.
random
()
*
8
-
4
;
const
angle
=
(
Math
.
random
()
-
0.5
)
*
0.4
;
ctx
.
save
();
ctx
.
translate
(
x
,
y
);
ctx
.
rotate
(
angle
);
// 随机颜色
const
hue
=
Math
.
floor
(
Math
.
random
()
*
360
);
ctx
.
fillStyle
=
`hsl(
${
hue
}
, 70%, 40%)`
;
ctx
.
fillText
(
captchaText
[
i
],
0
,
0
);
ctx
.
restore
();
}
// 添加干扰线
for
(
let
i
=
0
;
i
<
3
;
i
++
)
{
ctx
.
beginPath
();
ctx
.
strokeStyle
=
`rgba(
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
, 0.5)`
;
ctx
.
lineWidth
=
1
;
ctx
.
moveTo
(
Math
.
random
()
*
canvas
.
width
,
Math
.
random
()
*
canvas
.
height
);
ctx
.
lineTo
(
Math
.
random
()
*
canvas
.
width
,
Math
.
random
()
*
canvas
.
height
);
ctx
.
stroke
();
}
// 添加噪点
for
(
let
i
=
0
;
i
<
50
;
i
++
)
{
const
x
=
Math
.
random
()
*
canvas
.
width
;
const
y
=
Math
.
random
()
*
canvas
.
height
;
ctx
.
fillStyle
=
`rgba(
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
, 0.5)`
;
ctx
.
fillRect
(
x
,
y
,
2
,
2
);
}
return
{
text
:
captchaText
,
image
:
canvas
.
toDataURL
(),
};
};
const
captchaText
=
ref
(
""
);
const
captchaImage
=
ref
(
""
);
const
fileList
=
ref
([]);
const
uploadRef
=
ref
();
const
formData
=
ref
({
name
:
""
,
title
:
""
,
content
:
""
,
phoneNumber
:
""
,
// contentImg: "",
captcha
:
""
,
});
const
refreshCaptcha
=
()
=>
{
const
captcha
=
generateCaptcha
();
...
...
@@ -277,7 +229,14 @@ function handleExceed(files: any, fileList: any) {
}
//文件移除回调
function
beforeRemove
(
file
:
any
,
fileList
:
any
)
{
return
ElMessageBox
.
confirm
(
`确定移除
${
file
.
name
}
这个文件?`
);
// var index = -1;
// index = formData.value.contentImg.findIndex(function (text) {
// return text.uid == file.uid;
// });
// if (index != -1) {
// formData.value.contentImg.splice(index, 1);
// }
// return ElMessageBox.confirm(`确定移除 ${file.name}这个文件?`);
}
//文件上传成功回调
function
handleSuccess
()
{
...
...
@@ -287,25 +246,39 @@ function handleSuccess() {
});
}
const
formData
=
ref
({
name
:
""
,
title
:
""
,
content
:
""
,
phone
:
""
,
captcha
:
""
,
});
//提交表单
const
handleSubmit
=
()
=>
{
if
(
!
formData
.
value
.
name
||
!
formData
.
value
.
title
||
!
formData
.
value
.
content
||
!
formData
.
value
.
phone
||
!
formData
.
value
.
captcha
)
{
if
(
!
formData
.
value
.
name
)
{
ElMessage
({
message
:
"请填写姓名"
,
type
:
"warning"
,
});
return
;
}
if
(
!
formData
.
value
.
phoneNumber
)
{
ElMessage
({
message
:
"请填写联系方式"
,
type
:
"warning"
,
});
return
;
}
if
(
!
formData
.
value
.
title
)
{
ElMessage
({
message
:
"请填写留言标题"
,
type
:
"warning"
,
});
return
;
}
if
(
!
formData
.
value
.
content
)
{
ElMessage
({
message
:
"请填写必填项"
,
message
:
"请填写留言内容"
,
type
:
"warning"
,
});
return
;
}
if
(
!
formData
.
value
.
captcha
)
{
ElMessage
({
message
:
"请填写验证码"
,
type
:
"warning"
,
});
return
;
...
...
@@ -319,7 +292,13 @@ const handleSubmit = () => {
formData
.
value
.
captcha
=
""
;
return
;
}
// uploadRef.value.submit();
//将fileList中的文件逗号拼接,赋值给FormData.contentImg
// formData.value.contentImg = fileList.value.map((file) => file.raw).join(",");
// const formData = new FormData();
// fileList.value.forEach((file) => {
// formData.append("file", file.raw);
// });
// 提交表单
getWantMessage
(
formData
.
value
).
then
((
res
)
=>
{
ElMessage
({
...
...
@@ -331,13 +310,73 @@ const handleSubmit = () => {
name
:
""
,
title
:
""
,
content
:
""
,
phone
:
""
,
phoneNumber
:
""
,
contentImg
:
""
,
captcha
:
""
,
};
uploadRef
.
value
.
clearFiles
();
// 清空文件列表
refreshCaptcha
();
});
};
const
letters
=
Array
.
from
(
"ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
);
const
generateCaptcha
=
()
=>
{
const
canvas
=
document
.
createElement
(
"canvas"
);
const
ctx
=
canvas
.
getContext
(
"2d"
);
if
(
!
ctx
)
return
""
;
canvas
.
width
=
128
;
canvas
.
height
=
40
;
// 设置背景
ctx
.
fillStyle
=
"#fff"
;
ctx
.
fillRect
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
// 生成验证码文字
const
captchaText
=
Array
.
from
(
{
length
:
4
},
()
=>
letters
[
Math
.
floor
(
Math
.
random
()
*
letters
.
length
)]
).
join
(
""
);
// 绘制文字
ctx
.
font
=
"bold 24px Arial"
;
ctx
.
textBaseline
=
"middle"
;
// 随机颜色和位置绘制每个字符
for
(
let
i
=
0
;
i
<
captchaText
.
length
;
i
++
)
{
const
x
=
20
+
i
*
25
;
const
y
=
20
+
Math
.
random
()
*
8
-
4
;
const
angle
=
(
Math
.
random
()
-
0.5
)
*
0.4
;
ctx
.
save
();
ctx
.
translate
(
x
,
y
);
ctx
.
rotate
(
angle
);
// 随机颜色
const
hue
=
Math
.
floor
(
Math
.
random
()
*
360
);
ctx
.
fillStyle
=
`hsl(
${
hue
}
, 70%, 40%)`
;
ctx
.
fillText
(
captchaText
[
i
],
0
,
0
);
ctx
.
restore
();
}
// 添加干扰线
for
(
let
i
=
0
;
i
<
3
;
i
++
)
{
ctx
.
beginPath
();
ctx
.
strokeStyle
=
`rgba(
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
, 0.5)`
;
ctx
.
lineWidth
=
1
;
ctx
.
moveTo
(
Math
.
random
()
*
canvas
.
width
,
Math
.
random
()
*
canvas
.
height
);
ctx
.
lineTo
(
Math
.
random
()
*
canvas
.
width
,
Math
.
random
()
*
canvas
.
height
);
ctx
.
stroke
();
}
// 添加噪点
for
(
let
i
=
0
;
i
<
50
;
i
++
)
{
const
x
=
Math
.
random
()
*
canvas
.
width
;
const
y
=
Math
.
random
()
*
canvas
.
height
;
ctx
.
fillStyle
=
`rgba(
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
,
${
Math
.
random
()
*
255
}
, 0.5)`
;
ctx
.
fillRect
(
x
,
y
,
2
,
2
);
}
return
{
text
:
captchaText
,
image
:
canvas
.
toDataURL
(),
};
};
</
script
>
<
style
scoped
>
:deep
(
.el-input__wrapper
)
{
...
...
src/views/wm/message/MessageDetail.vue
View file @
ff20fbee
...
...
@@ -11,31 +11,34 @@
<!-- 主要内容卡片 -->
<div
class=
"rounded-lg bg-white p-6 shadow-sm"
>
<!-- 标题区域 -->
<div
class=
"mb-
6
"
>
<h1
class=
"mb-
4
text-xl font-medium text-gray-900"
>
东湖新区排水管网改造工程环境污染问题
<div
class=
"mb-
3
"
>
<h1
class=
"mb-
2
text-xl font-medium text-gray-900"
>
{{
detail
.
title
}}
</h1>
<div
class=
"flex items-center space-x-4 text-sm text-gray-500"
>
<span
class=
"inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700"
<!--
<span
class=
"inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-green-600"
>
{{
detail
.
replyStatus
==
"1"
?
"已回复"
:
"未回复"
}}
</span>
-->
<el-tag
size=
"small"
class=
"inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-green-600"
:type=
"detail.replyStatus === '1' ? 'success' : 'warning'"
>
已回复
</span>
<span>
留言人: 李**
</span>
<span>
时间: 2024年1月15日 09:23
</span>
{{
detail
.
replyStatus
===
"1"
?
"已回复"
:
"未回复"
}}
</el-tag>
<span>
留言人:
{{
detail
.
name
}}
</span>
<span>
时间:
{{
detail
.
contentDatetime
}}
</span>
</div>
</div>
<!-- 问题描述 -->
<div
class=
"mb-6"
>
<p
class=
"text-gray-600"
>
施工扬尘污染问题严重影响了周边居民的正常生活,希望相关部门能够重视此事。主要存在以下问题:
<br
/>
施工现场未按要求设置防尘设施
<br
/>
运输车辆未做好防尘措施
<br
/>
现场积尘较多,未及时清理
<br
/>
建议加强监管力度,落实各项防尘措施,保障居民生活环境。
</p>
<div
class=
"space-y-4 text-gray-600"
v-html=
"detail.content"
></div>
<!-- 附件区域 -->
<div
<
!--
<
div
v-if=
"attachments.length > 0"
class=
"mt-1 border-t border-gray-200 pt-3"
>
...
...
@@ -49,7 +52,7 @@
class=
"group flex cursor-pointer items-center rounded-md p-1 hover:bg-gray-50"
@
click=
"handlePreview(file)"
>
<el-icon
class=
"mr-2 text-gray-400"
color=
"
blue
"
>
<el-icon
class=
"mr-2 text-gray-400"
color=
"
gray
"
>
<Document
v-if=
"
file.type === 'pdf' ||
...
...
@@ -71,7 +74,7 @@
}}
</span>
</div>
</div>
</div>
</div>
-->
</div>
<!-- 预览弹窗 -->
<el-dialog
...
...
@@ -118,20 +121,9 @@
</el-dialog>
<!-- 官方回复 -->
<div
class=
"rounded-lg bg-gray-50 p-5"
>
<div
class=
"rounded-lg bg-gray-50 p-5"
v-if=
"detail.reply != null"
>
<h2
class=
"mb-4 text-lg font-medium text-red-500"
>
官方回复:
</h2>
<div
class=
"space-y-4 text-gray-600"
>
<p>
网友您好!收到您的投诉后,我局高度重视,立即组织相关部门进行实地调查和处理。经查:
<br
/>
1.
关于城东新区排水管网改造工程施工现场扬尘污染问题,我局已要求施工单位立即采取以下整改措施:
<br
/>
增设雾炮机和喷淋设备,加强施工现场降尘
<br
/>
对裸露土地进行苫盖,设置防尘网
<br
/>
增加洒水车频次,保持路面湿润
<br
/>
2.
目前整改措施已全部落实到位,扬尘污染问题得到有效控制。后续我局将继续加强监管,确保施工期间不发生扬尘污染问题。
<br
/>
感谢您对我们工作的监督,我们将持续改进服务质量。
</p>
</div>
<div
class=
"space-y-4 text-gray-600"
v-html=
"detail.reply"
></div>
</div>
</div>
</div>
...
...
@@ -170,8 +162,12 @@ const breadcrumbItems2 = ref([
{
title
:
"详情"
,
path
:
""
},
]);
const
detail
=
ref
({});
// 获取留言详情
getMessageDetail
(
route
.
query
.
id
).
then
((
res
)
=>
{});
getMessageDetail
(
route
.
query
.
id
).
then
((
res
)
=>
{
detail
.
value
=
res
.
data
;
});
interface
FileItem
{
name
:
string
;
...
...
src/views/wm/message/MessageList.vue
View file @
ff20fbee
<!-- 代码已包含 CSS:使用 TailwindCSS , 安装 TailwindCSS 后方可看到布局样式效果 -->
<
template
>
<div
class=
"w-[1440px] mx-auto px-4"
>
<!-- 面包屑导航 -->
<div
class=
"flex items-center justify-between py-2 border-b mb-5"
>
<bread-crumb
:breadcrumbItems=
"breadcrumbItems"
/>
<div
class=
"w-[1440px] mx-auto px-4"
>
<!-- 面包屑导航 -->
<div
class=
"flex items-center justify-between py-2 border-b mb-5"
>
<bread-crumb
:breadcrumbItems=
"breadcrumbItems"
/>
<!-- 搜索框 -->
<div
class=
"relative"
>
<input
type=
"text"
v-model=
"searchText"
placeholder=
"搜索留言"
class=
"w-[300px] h-8 pl-4 pr-10 rounded-lg border-none bg-white shadow-sm focus:ring-2 focus:ring-blue-500 text-sm"
/>
<el-icon
class=
"absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
><Search
/></el-icon>
</div>
<!-- 搜索框 -->
<div
class=
"relative"
>
<input
type=
"text"
v-model=
"searchText"
placeholder=
"搜索留言"
class=
"w-[300px] h-8 pl-4 pr-10 rounded-lg border-none bg-white shadow-sm focus:ring-2 focus:ring-blue-500 text-sm"
@
keyup
.
enter=
"handleSearch"
/>
<el-icon
class=
"absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
><Search
/></el-icon>
</div>
<!-- 留言列表 -->
<div
class=
"space-y-4"
>
<div
v-for=
"(item, index) in messageList"
:key=
"index"
class=
"bg-white rounded-lg shadow-sm p-6 transition-all duration-300 hover:shadow-lg hover:scale-[1.01] cursor-pointer"
@
click=
"clickList(item.id)"
>
<h2
class=
"text-lg font-medium text-gray-900 mb-3"
>
{{
item
.
title
}}
</h2>
<p
class=
"text-gray-600 mb-4 leading-relaxed"
>
{{
item
.
content
}}
</p>
<div
class=
"flex items-center text-sm text-gray-500"
>
<span>
{{
item
.
date
}}
</span>
<span
class=
"mx-2"
>
留言人:
{{
item
.
author
}}
</span>
</div>
</div>
<!-- 留言列表 -->
<div
class=
"space-y-4"
>
<div
v-for=
"(item, index) in hotList"
:key=
"index"
class=
"bg-white rounded-lg shadow-sm p-6 transition-all duration-300 hover:shadow-lg hover:scale-[1.01] cursor-pointer"
@
click=
"clickList(item.id)"
>
<!--
<h2
class=
"text-lg font-medium text-gray-900 mb-3"
>
{{
item
.
title
}}
</h2>
-->
<div
class=
"flex justify-between items-start mb-1"
>
<h3
class=
"text-lg font-medium text-gray-900"
>
{{
item
.
title
}}
</h3>
<span
:class=
"[
'px-3 py-1 rounded-full text-sm',
item.replyStatus === '1'
? 'bg-green-50 text-green-600'
: 'bg-orange-50 text-orange-600',
]"
>
{{
item
.
replyStatus
===
"1"
?
"已回复"
:
"未回复"
}}
</span
>
</div>
<p
class=
"text-gray-600 mb-4 leading-relaxed"
>
{{
item
.
content
}}
</p>
<div
class=
"flex items-center text-sm text-gray-500"
>
<span>
{{
item
.
contentDatetime
}}
</span>
<span
class=
"mx-5"
>
留言人:
{{
item
.
name
}}
</span>
</div>
</div>
<!-- 分页 -->
<div
class=
"flex justify-center mt-8"
>
<el-pagination
v-model:current-page=
"pageNo"
v-model:page-size=
"pageSize"
background
:total=
"total"
:page-sizes=
"[10, 20, 30, 40]"
layout=
"prev, pager, next"
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
/>
</div>
</div>
<!-- 分页 -->
<div
class=
"flex justify-center mt-8"
>
<el-pagination
v-model:current-page=
"pageNo"
v-model:page-size=
"pageSize"
background
:total=
"total"
:page-sizes=
"[10, 20, 30, 40]"
layout=
"prev, pager, next"
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
/>
</div>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
ref
}
from
"vue"
;
import
{
useRouter
}
from
"vue-router"
;
import
{
ref
,
watch
}
from
"vue"
;
import
{
useRouter
,
useRoute
}
from
"vue-router"
;
import
{
Search
,
ArrowRight
}
from
"@element-plus/icons-vue"
;
import
BreadCrumb
from
"@/components/breadcrumb/BreadCrumb.vue"
;
import
{
getHotList
}
from
"@/api/wm/wm"
;
import
{
onMounted
}
from
"vue"
;
const
router
=
useRouter
();
const
searchText
=
ref
(
""
);
const
route
=
useRoute
();
const
searchText
=
ref
();
const
pageNo
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
const
hotList
=
ref
([]);
...
...
@@ -76,11 +92,35 @@ const handleCurrentChange = (val: number) => {
getList
(
pageSize
.
value
,
pageNo
.
value
);
};
getList
(
pageSize
.
value
,
pageNo
.
value
);
function
handleSearch
()
{
getList
(
10
,
1
);
}
onMounted
(()
=>
{
if
(
route
.
query
.
keywords
)
{
searchText
.
value
=
route
.
query
.
keywords
;
console
.
log
(
route
.
query
.
keywords
);
}
getList
(
pageSize
.
value
,
pageNo
.
value
);
});
// watch route.query.keywords
watch
(
()
=>
route
.
query
.
keywords
,
(
newVal
)
=>
{
searchText
.
value
=
newVal
;
getList
(
pageSize
.
value
,
pageNo
.
value
);
}
);
//获取新闻热点列表
function
getList
(
pageSize
:
number
,
pageNo
:
number
)
{
// if (route.query.keywords) {
// searchText.value = route.query.keywords;
// console.log(route.query.keywords);
// }
const
datas
=
{
title
:
searchText
.
value
,
pageSize
:
pageSize
,
pageNo
:
pageNo
,
};
...
...
@@ -96,36 +136,6 @@ const breadcrumbItems = ref([
{
title
:
"留言列表"
},
]);
const
messageList
=
ref
([
{
title
:
"反映某建筑公司拖欠农民工工资问题"
,
content
:
"太原市建筑工人反映某建筑公司拖欠其与工友共计 50 余人约 156 万元工资。经劳动监察部门介入调查,目前该公司已全额支付拖欠资金,并按规定支付赔偿金。"
,
date
:
"2024-12-27 09:41"
,
author
:
"张先生"
,
},
{
title
:
"某快递公司拖欠快递员工资及社保问题"
,
content
:
"市民李师傅反映某快递公司拖欠员工工资 3 个月,且未缴纳社会保险。经调查核实并约谈该公司负责人,该公司已补发全部工资并补缴社保,承诺今后按时发放工资。"
,
date
:
"2024-12-02 12:24"
,
author
:
"李先生"
,
},
{
title
:
"反映某餐饮企业克扣服务员工资问题"
,
content
:
"服务员投诉某连锁餐饮企业以各种理由克扣工资。经查实,该企业存在违规罚款、未按约定发放绩效工资等问题。目前已责令整改,并补发被克扣工资。"
,
date
:
"2024-12-12 09:02"
,
author
:
"张女士"
,
},
{
title
:
"某科技公司拖欠开发人员工资及年终奖"
,
content
:
"工程师等 12 名技术人员投诉某科技公司拖欠 2023 年 11-12 月工资及年终奖共计 89 万元。经多方协调,该公司已筹措资金完成全部支付。"
,
date
:
"2024-12-11 15:00"
,
author
:
"王先生"
,
},
]);
function
clickList
(
id
:
number
)
{
router
.
push
({
path
:
"/wm/messagedetail"
,
...
...
src/views/wm/recover/RecoverList.vue
View file @
ff20fbee
...
...
@@ -6,7 +6,6 @@
<!-- 面包屑导航 -->
<div
class=
"flex items-center justify-between py-2 border-b mb-5"
>
<bread-crumb
:breadcrumbItems=
"breadcrumbItems"
/>
<!-- 搜索框 -->
<div
class=
"relative"
>
<input
...
...
@@ -14,6 +13,7 @@
v-model=
"searchText"
placeholder=
"搜索回复"
class=
"w-[300px] h-8 pl-4 pr-10 rounded-lg border-none bg-white shadow-sm focus:ring-2 focus:ring-blue-500 text-sm"
@
keyup
.
enter=
"handleSearch"
/>
<el-icon
class=
"absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
...
...
@@ -24,25 +24,31 @@
<!-- 回复列表 -->
<div>
<div
v-for=
"(item, index) in
displayItems
"
v-for=
"(item, index) in
recoverList
"
:key=
"index"
class=
"bg-white rounded-lg p-6 mb-4 shadow-sm hover:shadow-md transition-shadow"
@
click=
"clickList(item.id)"
>
<div
class=
"flex justify-between items-start mb-
3
"
>
<div
class=
"flex justify-between items-start mb-
1
"
>
<h3
class=
"text-lg font-medium text-gray-900"
>
{{
item
.
title
}}
</h3>
<span
:class=
"[
'px-3 py-1 rounded-full text-sm',
item.
status === '已处理
'
item.
replyStatus === '1
'
? 'bg-green-50 text-green-600'
: 'bg-orange-50 text-orange-600',
]"
>
{{
item
.
status
}}
</span
>
{{
item
.
replyStatus
===
"1"
?
"已回复"
:
"未回复"
}}
</span
>
</div>
<p
class=
"text-gray-600 mb-4"
>
{{
item
.
content
}}
</p>
<div
class=
"text-sm text-gray-400"
>
{{
item
.
time
}}
</div>
<!--
<p
class=
"text-gray-600 mb-4"
>
{{
item
.
content
}}
</p>
<div
class=
"text-sm text-gray-400"
>
{{
item
.
contentDatetime
}}
</div>
-->
<div
class=
"flex items-center text-sm text-gray-500"
>
<span>
{{
item
.
contentDatetime
}}
</span>
<span
class=
"mx-5"
>
留言人:
{{
item
.
name
}}
</span>
</div>
<p
class=
"mt-2 text-gray-700"
>
{{
item
.
content
}}
</p>
</div>
</div>
...
...
@@ -89,9 +95,13 @@ const handleCurrentChange = (val: number) => {
getList
(
pageSize
.
value
,
pageNo
.
value
);
function
handleSearch
()
{
getList
(
10
,
1
);
}
//获取新闻热点列表
function
getList
(
pageSize
:
number
,
pageNo
:
number
)
{
const
datas
=
{
title
:
searchText
.
value
,
pageSize
:
pageSize
,
pageNo
:
pageNo
,
};
...
...
@@ -107,58 +117,9 @@ const breadcrumbItems = ref([
{
title
:
"回复列表"
},
]);
const
allItems
=
[
{
title
:
"关于东湖公园设施改善的回复"
,
content
:
"感谢您的建议。我局已安排相关部门实地考察,将在东湖公园增设 25 组休闲椅和 10 处遮阳凉亭,预计本月内完工。"
,
time
:
"2024-12-27 09:41"
,
status
:
"已处理"
,
},
{
title
:
"关于龙泉山交通问题的回复"
,
content
:
"关于龙泉山拥堵问题,我们已制定改善方案:1. 优化信号灯配时;2. 增设潮汐车道;3. 加强交通疏导。预计一周内见效。"
,
time
:
"2024-12-02 12:24"
,
status
:
"已处理"
,
},
{
title
:
"关于金融大厦停车问题的回复"
,
content
:
"金融大厦地下停车场扩建工程已列入今年重点项目,将新增车位 600 个,预计 6 月完工。"
,
time
:
"2024-12-02 12:24"
,
status
:
"已处理"
,
},
{
title
:
"关于市民广场交通设施的回复"
,
content
:
"我们将对市民广场周边道路进行优化:1. 新增人行天桥;2. 扩建非机动车道;3. 增设智能红绿灯。预计两周内完成。"
,
time
:
"2024-12-02 12:24"
,
status
:
"未处理"
,
},
{
title
:
"关于青山湖环境整治的回复"
,
content
:
"青山湖环境整治工程已启动,包括水质净化、垃圾清理、绿化提升等多项工作,预计三个月内完成。"
,
time
:
"2024-12-02 12:24"
,
status
:
"未处理"
,
},
];
const
totalItems
=
computed
(()
=>
allItems
.
length
);
const
displayItems
=
computed
(()
=>
{
const
start
=
(
pageNo
.
value
-
1
)
*
pageSize
.
value
;
const
filtered
=
allItems
.
filter
(
(
item
)
=>
item
.
title
.
includes
(
searchText
.
value
)
||
item
.
content
.
includes
(
searchText
.
value
)
);
return
filtered
.
slice
(
start
,
start
+
pageSize
.
value
);
});
function
clickList
(
id
:
number
)
{
router
.
push
({
path
:
"/wm/
recover
detail"
,
path
:
"/wm/
message
detail"
,
query
:
{
title
:
"回复列表"
,
pathType
:
"/wm/recoverlist"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment