This commit is contained in:
lcw
2026-03-24 12:18:39 +08:00
parent c181530639
commit 60de16032f
49 changed files with 16031 additions and 12706 deletions

25494
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,10 @@
"xlsx": "^0.18.5"
},
"devDependencies": {
"@babel/core": "^7.29.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/preset-env": "^7.29.2",
"@element-plus/icons": "^0.0.11",
"@toast-ui/editor": "^3.0.2",
"@vue/cli-plugin-babel": "~4.5.0",

View File

@ -155,7 +155,7 @@ header {
}
&::v-deep .el-form-item--default {
width: 23%;
// width: 23%;
padding-bottom: 20px;
margin: 0 1%;
}

BIN
src/assets/images/01.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/02.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/03.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/04.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/05.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/06.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/07.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/08.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/09.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/10.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/11.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/12.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/13.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/14.mp3 Normal file

Binary file not shown.

BIN
src/assets/images/16.mp3 Normal file

Binary file not shown.

View File

@ -12,7 +12,7 @@
<el-input-number v-model="listQuery[item.prop]" v-else-if="item.type == 'number'" :step="item.step || 1" style="width: 100%" :min="item.min || 0" :max="item.max || 1000" :disabled="item.disabled" />
<!--选择 select-->
<MOSTY.Select v-else-if="item.type == 'select'" filterable :multiple="item.multiple" v-model="listQuery[item.prop]" :dictEnum="item.options" width="100%" clearable :placeholder="`请选择${item.label}`" :disabled="item.disabled" />
<MOSTY.Select v-else-if="item.type == 'select'" :filterable="item.filterable" :multiple="item.multiple" v-model="listQuery[item.prop]" :dictEnum="item.options" width="100%" clearable :placeholder="`请选择${item.label}`" :disabled="item.disabled" />
<!-- 部门department -->
<template v-else-if="item.type === 'department'">

View File

@ -30,20 +30,40 @@ const countdown = ref(0) // 倒计时时间(秒)
// 音频播放器实例映射
const audioPlayers = ref({
'01': null, // 预警信息
'02': null, // 信息上报
'03': null, // 研判审批
'04': null, // 研判指令
'05': null, // 线索下发,
'06': null, // 警情监测
'07': null, // 线索处理
'08': null, // 线索下发
'09': null, // 线索处理
'10': null, // 林安码
'11': null, // 发布了新的线索
'12': null, // 有新的研判指令
'13': null, // 有新的研判约稿通知
'14': null, // 有新的公文发布
'15': null, // 有新的待审核工作(补发音效)
})
// 音频文件路径映射
const audioPaths = {
'02': require('@/assets/images/cjyp.mp3'),
'03': require('@/assets/images/ypbg.mp3'),
'04': require('@/assets/images/ypzl.mp3'),
'05': require('@/assets/images/xsyp.mp3'),
'06': require('@/assets/images/jqjc.mp3')
'01': require('@/assets/images/01.mp3'),//高级预计信息前置
'02': require('@/assets/images/02.mp3'),//一般预警信息前置
'03': require('@/assets/images/03.mp3'),//信息前置
'04': require('@/assets/images/04.mp3'),//红色预警
'05': require('@/assets/images/05.mp3'),//新的重点人
'06': require('@/assets/images/06.mp3'),//一级临控预警
'07': require('@/assets/images/07.mp3'),//有新的布控预警情
'08': require('@/assets/images/08.mp3'),//有新的标签预警
'09': require('@/assets/images/09.mp3'),//信息汇聚系统有新信息
'10': require('@/assets/images/10.mp3'),//林安码
'11': require('@/assets/images/11.mp3'),//发布了新的线索
'12': require('@/assets/images/12.mp3'),//有新的研判指令
'13': require('@/assets/images/13.mp3'),//有新的研判约稿通知
'14': require('@/assets/images/14.mp3'),//有新的公文发布
'15': require('@/assets/images/16.mp3'),//有新的警情监测预警,请注意查收
}
// 初始化音频播放器
@ -59,14 +79,85 @@ const initAudioPlayers = () => {
}
// 根据类型播放音频
const playAudioByType = (type) => {
if (audioPlayers.value[type]) {
try {
audioPlayers.value[type].play()
} catch (error) {
console.error(`播放类型${type}的音频失败:`, error)
const playAudioByType = (val) => {
switch (val.typeMasgeLx) {
case '01'://预警
// 01 布控预警、02 七类重点人、03 政保
switch (val.yjlb) {
case '01':
switch (val.yjJb) {
case '01':
audioPlayers.value['01'].play()
audioPlayers.value['06'].play()
break;
default:
audioPlayers.value['02'].play()
audioPlayers.value['07'].play()
break;
}
break
case '02':
switch (val.yjJb) {
case '01':
audioPlayers.value['01'].play()
audioPlayers.value['04'].play()
break;
default:
audioPlayers.value['02'].play()
audioPlayers.value['05'].play()
break;
}
break
case '03':
break
}
break
case '02'://信息汇聚
audioPlayers.value['03'].play()
audioPlayers.value['09'].play()
break
case '03'://约稿
audioPlayers.value['03'].play()
audioPlayers.value['13'].play()
break
case '04'://指令
audioPlayers.value['03'].play()
audioPlayers.value['12'].play()
break
case '05'://新线索
audioPlayers.value['03'].play()
audioPlayers.value['11'].play()
break
case '06'://监测
audioPlayers.value['02'].play()
audioPlayers.value['15'].play()
break
// case '07':
// audioPlayers.value['07'].play()
// break
case '08'://林安码
audioPlayers.value['03'].play()
audioPlayers.value['10'].play()
break
// case '10':
// audioPlayers.value['10'].play()
// break
default:
break
}
// if (audioPlayers.value[type]) {
// try {
// audioPlayers.value[type].play()
// } catch (error) {
// console.error(`播放类型${type}的音频失败:`, error)
// }
// }
}
// 手动关闭
const handleClose = () => {
@ -108,7 +199,7 @@ onMounted(() => {
dataList.value = newsDate
// dataList.value.unshift({...newsDate.data,typeMasgeLx:newsDate.type})
// 根据消息类型播放音频
playAudioByType(newsDate[0].typeMasgeLx)
playAudioByType(newsDate[0])
resetCountdown()
}
})
@ -117,10 +208,10 @@ const idEntityCard = ref(getItem('idEntityCard'))
const dataModel = () => {
qcckGet({}, '/mosty-gsxt/dsjJbxx/message').then(res => {
if (res) {
const yjmasg = res.filter(item => item.type === '01')
if (yjmasg.length > 0) {
emitter.emit('openYp', yjmasg[0].obj); // 触发音频播放
} else {
// const yjmasg = res.filter(item => item.type === '01')
// if (yjmasg.length > 0) {
// emitter.emit('openYp', yjmasg[0].obj); // 触发音频播放
// }
const data = res.filter(item => item.sfzList.includes(idEntityCard.value))
const infoMasge = data.map(item => {
return {
@ -128,10 +219,9 @@ const dataModel = () => {
typeMasgeLx: item.type
}
})
console.log(infoMasge,"xxxxxxxxxxxx");
console.log(infoMasge, "xxxxxxxxxxxx");
emitter.emit('webSocketMessage', infoMasge)
}
}
})
}

View File

@ -1,4 +1,12 @@
<template>
<div class="test-item" v-if="item.typeMasgeLx == '01'" @click="goDetail(item.id, item.typeMasgeLx, item)">
<div class="item-header">
<div class="item-title">{{ item.yjBt || '' }}</div>
<div class="item-type">{{ item.yjlb == '01' ? '布控预警' : item.yjlb == '02' ? '七类重点人' : '政保' }}</div>
</div>
<div class="item-message">{{ item.yjNr }}</div>
<div class="item-time">{{ item.yjSj || '' }}</div>
</div>
<div class="test-item" v-if="item.typeMasgeLx == '02'" @click="goDetail(item.id, item.typeMasgeLx)">
<div class="item-header">
<div class="item-title">{{ item.qbmc || '' }}</div>
@ -9,11 +17,11 @@
</div>
<div class="test-item" v-if="item.typeMasgeLx == '03'" @click="goDetail(item.id, item.typeMasgeLx)">
<div class="item-header">
<div class="item-title">{{ item.bgmc || '' }}</div>
<div class="item-title">{{ item.ypyt || '' }}</div>
<div class="item-type">{{ informationMap[item.typeMasgeLx] }}</div>
</div>
<div class="item-message" v-html="item.bgnr"></div>
<div class="item-time">{{ item.xtCjsj || '' }}</div>
<div class="item-message">{{ item.ssbm }}</div>
<div class="item-time">{{ item.ypsj || '' }}</div>
</div>
<div class="test-item" v-if="item.typeMasgeLx == '04'" @click="goDetail(item.id, item.typeMasgeLx)">
<div class="item-header">
@ -36,13 +44,22 @@
<div class="item-title">{{ item.gxdwmc || '' }}</div>
<div class="item-type">{{ informationMap[item.typeMasgeLx] }}</div>
</div>
<div class="item-message" >{{ item.bcjjnr }}</div>
<div class="item-message">{{ item.bcjjnr }}</div>
<div class="item-time">{{ item.bjsj || '' }}</div>
</div>
<div class="test-item" v-if="item.typeMasgeLx == '08'" @click="goDetail(item.id, item.typeMasgeLx)">
<div class="item-header">
<div class="item-title">{{ item.qbmc || '' }}</div>
<div class="item-type">{{ informationMap[item.typeMasgeLx] }}</div>
</div>
<div class="item-message">{{ item.qbnr }}</div>
<div class="item-time">{{ item.sxsbsj || '' }}</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import emitter from "@/utils/eventBus.js"; // 导入
const router = useRouter()
const props = defineProps({
item: {
@ -51,23 +68,27 @@ const props = defineProps({
}
})
const informationMap = {
'01': '警信息',
'01': '警信息',
'02': '信息上报',
'03': '研判审批',
'03': '研判约稿',
'04': '研判指令',
'05': '线索下发',
'06': '警情监测',
'08': '林安码信息',
}
const emit=defineEmits(['goDetail'])
const goDetail = (id, lx) => {
const emit = defineEmits(['goDetail'])
const goDetail = (id, lx, val) => {
let path = ''
switch (lx) {
case '01':
emitter.emit('openYp', val);
break;
case '02':
path = '/InfoCollection'
break;
case '03':
path = '/strategicResearchs'
path = '/dataReduction'
break;
default:
case '04':
path = '/judgmentCommand'
break;
@ -77,6 +98,9 @@ const goDetail = (id, lx) => {
case '06':
path = '/policeReport'
break;
case '08':
path = '/lamXs'
break;
}
router.push({
path: path,

View File

@ -29,8 +29,6 @@ const routes = computed(() => {
}
}
)
console.log(data);
return generateMenus(data);
});
if (!store.getters.token) {

View File

@ -918,10 +918,19 @@ export const publicRoutes = [
// ]
// },
// {
// path: "/forumPost2",
// name: "forumPost2",
// component: () => import("@/views/forumPost/index.vue"),
// meta: {
// title: "情报论坛",
// icon: "article-ranking"
// }
// },
{
path: "/forumPost",
name: "forumPost",
component: () => import("@/views/forumPost/index.vue"),
component: () => import("@/views/backOfficeSystem/luntan/index.vue"),
meta: {
title: "情报论坛",
icon: "article-ranking"

View File

@ -7,10 +7,9 @@
</Search>
</div>
<!-- 表格 -->
<div class="margTop">
<MyTable :tableData="pageData.tableData" :tableColumn="pageData.tableColumn" :tableHeight="pageData.tableHeight"
:key="pageData.keyCount" :tableConfiger="pageData.tableConfiger" :controlsWidth="pageData.controlsWidth"
@chooseData="chooseData">
<div class="margTop" style="padding: 0;">
<WarnDataTable :data="pageData.tableData" :columns="pageData.tableColumn" :tableHeight="pageData.tableHeight"
:loading="pageData.tableConfiger.loading" @selection-change="chooseData">
<template #bqList="{ row }">
<ul>
<li class="one_text_detail marks mb4" :key="index" v-for="(item, index) in row.bqList">{{ item.bqMc }}({{
@ -20,9 +19,7 @@
<template #ryXb="{ row }">
<DictTag :tag="false" :value="row.ryXb" :options="D_BZ_XB" />
</template>
<template #ryJg="{ row }">
<DictTag :tag="false" :value="row.ryJg" :options="D_BZ_XZQHDM" />
</template>
<template #ryMz="{ row }">
<DictTag :tag="false" :value="row.ryMz" :options="D_BZ_MZ" />
</template>
@ -47,12 +44,13 @@
<!-- 操作 -->
<template #controls="{ row }">
<el-link size="small" type="success" @click="handleSend(row.id)">移入</el-link>
<el-link size="small" type="success" @click="handleSend(row.id)">移入重点人</el-link>
<el-link size="small" type="success" @click="handleMoveToFocus(row.id)">移入关注库</el-link>
<el-link size="small" type="primary" @click="addEdit('edit', row)">编辑</el-link>
<el-link size="small" type="danger" @click="deleteRow(row.id)" v-if="isShiQzDelet">删除</el-link>
<el-link size="small" type="primary" @click="addEdit('detail', row)">详情</el-link>
</template>
</MyTable>
</WarnDataTable>
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
...pageData.pageConfiger,
total: pageData.total
@ -68,7 +66,7 @@
import { ElMessage } from "element-plus";
import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import WarnDataTable from "@/views/backOfficeSystem/ces/components/WarnDataTable.vue";
import Pages from "@/components/aboutTable/Pages.vue";
import Search from "@/components/aboutTable/Search.vue";
import AddForm from "@/views/backOfficeSystem/DeploymentDisposal/mpvPeo/components/addForm.vue";
@ -127,17 +125,18 @@ const pageData = reactive({
pageSize: 20,
pageCurrent: 1
},
controlsWidth: 150,
controlsWidth: 340,
tableColumn: [
{ label: "姓名", prop: "ryXm", width: 150 },
{ label: "性别", prop: "ryXb", showSolt: true, width: 100 },
{ label: "籍贯", prop: "ryJg", showSolt: true, width: 100 },
{ label: "性别", prop: "ryXb", slotName: "ryXb" },
{ label: "籍贯", prop: "ryJg", width: 100 },
{ label: "身份证", prop: "rySfzh", width: 200 },
{ label: "民族", prop: "ryMz", showSolt: true, width: 100 },
{ label: "户籍地区划", prop: "hjdQh", showSolt: true, width: 150 },
{ label: "户籍派出所", prop: "hjdPcsmc", width: 200 },
{ label: "民族", prop: "ryMz", slotName: "ryMz" },
{ label: "户籍地区划", prop: "hjdQh", width: 150, slotName: "hjdQh" },
{ label: "户籍派出所", prop: "hjdPcsmc" },
{ label: "户籍地详址", prop: "hjdXz", width: 200 },
{ label: "审核状态", prop: "zdrZt", showSolt: true },
{ label: "审核状态", prop: "zdrZt", slotName: "zdrZt" },
{ label: "操作", prop: "controls", slotName: "controls", width: 280 },
]
});
const isShiQzDelet = ref(false)
@ -188,7 +187,14 @@ const handleSend = (id) => {
})
};
const handleMoveToFocus = (id) => {
proxy.$confirm("确定要移至关注库?", "警告", { type: "warning" }).then(() => {
qcckPost({ id, rylx: '03' }, "/mosty-gsxt/tbGsxtZdry/rylxyd").then(() => {
proxy.$message({ type: "success", message: "移除成功" });
getList();
});
})
}
const chooseData = (data) => {
ids.value = Array.isArray(data) ? data.map((item) => item.id) : [];
choosList.value = Array.isArray(data) ? data : [];

View File

@ -2,7 +2,7 @@
<div>
<!-- 搜索 -->
<div ref="searchBox" class="mt10">
<Search :searchArr="searchConfiger" @submit="onSearch" >
<Search :searchArr="searchConfiger" @submit="onSearch">
<el-button type="primary" size="small" @click="addEdit('add', '')">
<el-icon style="vertical-align: middle">
<CirclePlus />
@ -13,10 +13,9 @@
</div>
<!-- 表格 -->
<div class="margTop">
<MyTable :tableData="pageData.tableData" :tableColumn="pageData.tableColumn" :tableHeight="pageData.tableHeight"
:key="pageData.keyCount" :tableConfiger="pageData.tableConfiger" :controlsWidth="pageData.controlsWidth"
@chooseData="chooseData">
<div class="margTop" style="padding: 0;">
<WarnDataTable :data="pageData.tableData" :columns="pageData.tableColumn" :tableHeight="pageData.tableHeight"
:loading="pageData.tableConfiger.loading" @selection-change="chooseData">
<template #bqList="{ row }">
<ul>
<li class="one_text_detail marks mb4" :key="index" v-for="(item, index) in row.bqList">{{ item.bqMc }}({{
@ -54,7 +53,8 @@
<!-- 操作 -->
<template #controls="{ row }">
<el-link size="small" type="success" @click="handleremove(row.id)"></el-link>
<el-link size="small" type="success" @click="handleremove(row.id)">入基础库</el-link>
<el-link size="small" type="success" @click="handleZdr(row.id)">移入重点人</el-link>
<el-link size="small" type="success" v-if="row.zdrZt == '01' || row.zdrZt == '03'"
@click="handleSend(row.id)">送审</el-link>
<el-link size="small" type="primary" v-if="row.zdrZt == '01' || row.zdrZt == '03'"
@ -62,7 +62,7 @@
<el-link size="small" type="primary" @click="addEdit('detail', row)">详情</el-link>
<el-link size="small" type="danger" @click="deleteRow(row.id)">删除</el-link>
</template>
</MyTable>
</WarnDataTable>
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
...pageData.pageConfiger,
total: pageData.total
@ -84,7 +84,7 @@ import { ElMessage } from "element-plus";
import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
import ZxsForm from "./components/zxsForm.vue";
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import WarnDataTable from "@/views/backOfficeSystem/ces/components/WarnDataTable.vue";
import Pages from "@/components/aboutTable/Pages.vue";
import Search from "@/components/aboutTable/Search.vue";
import AddForm from "./components/addForm.vue";
@ -94,8 +94,8 @@ import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const { proxy } = getCurrentInstance();
const { D_ZDRGK_GKZT,D_GS_ZDQT_ZT, D_GS_ZDR_RYJB, D_BZ_XB, D_BZ_MZ, D_BZ_XZQHDM, D_GS_ZDR_BK_ZT, D_GS_ZDR_CZZT, D_GS_BQ_ZL, D_GS_BQ_LB, D_GS_BQ_LX, D_GS_ZDR_YJDJ, D_GS_BK_SSJZ, D_GS_BK_SQLX, D_BZ_SF, D_GS_XS_LY, D_BZ_SSZT, D_GS_XS_LX, D_GS_XS_QTLX } =
proxy.$dict('D_ZDRGK_GKZT',"D_GS_ZDQT_ZT", "D_GS_ZDR_RYJB", "D_BZ_XB", "D_BZ_MZ", "D_BZ_XZQHDM", "D_GS_ZDR_BK_ZT", "D_GS_ZDR_CZZT", "D_GS_BQ_ZL", "D_GS_BQ_LB", "D_GS_BQ_LX", "D_GS_ZDR_YJDJ", "D_GS_BK_SSJZ", "D_GS_BK_SQLX", "D_BZ_SF", "D_GS_XS_LY", "D_BZ_SSZT", "D_GS_XS_LX", "D_GS_XS_QTLX");
const { D_ZDRGK_GKZT, D_GS_ZDQT_ZT, D_GS_ZDR_RYJB, D_BZ_XB, D_BZ_MZ, D_BZ_XZQHDM, D_GS_ZDR_BK_ZT, D_GS_ZDR_CZZT, D_GS_BQ_ZL, D_GS_BQ_LB, D_GS_BQ_LX, D_GS_ZDR_YJDJ, D_GS_BK_SSJZ, D_GS_BK_SQLX, D_BZ_SF, D_GS_XS_LY, D_BZ_SSZT, D_GS_XS_LX, D_GS_XS_QTLX } =
proxy.$dict('D_ZDRGK_GKZT', "D_GS_ZDQT_ZT", "D_GS_ZDR_RYJB", "D_BZ_XB", "D_BZ_MZ", "D_BZ_XZQHDM", "D_GS_ZDR_BK_ZT", "D_GS_ZDR_CZZT", "D_GS_BQ_ZL", "D_GS_BQ_LB", "D_GS_BQ_LX", "D_GS_ZDR_YJDJ", "D_GS_BK_SSJZ", "D_GS_BK_SQLX", "D_BZ_SF", "D_GS_XS_LY", "D_BZ_SSZT", "D_GS_XS_LX", "D_GS_XS_QTLX");
const obj = ref({});
const showzxs = ref(false);
const zxsDilof = ref();
@ -148,18 +148,19 @@ const pageData = reactive({
pageSize: 20,
pageCurrent: 1
},
controlsWidth: 250,
controlsWidth: 280,
tableColumn: [
{ label: "姓名", prop: "ryXm",width: 100 },
{ label: "性别", prop: "ryXb", showSolt: true, width: 80 },
{ label: "姓名", prop: "ryXm", width: 100 },
{ label: "性别", prop: "ryXb", slotName: "ryXb", width: 80 },
{ label: "身份证", prop: "rySfzh", width: 170 },
{ label: "民族", prop: "ryMz", showSolt: true, width: 80 },
{ label: "民族", prop: "ryMz", slotName: "ryMz", width: 80 },
{ label: "户籍派出所", prop: "hjdPcsmc" },
{ label: "标签", prop: "bqList", showSolt: true, showOverflowTooltip: true },
{ label: "标签", prop: "bqList", slotName: "bqList", showOverflowTooltip: true },
{ label: "管辖单位", prop: "gxSsbmmc" },
{ label: "管控状态", prop: "zdrBkZt", showOverflowTooltip: true,showSolt: true, width: 100 },
{ label: "审核状态", prop: "zdrZt", showSolt: true, width: 100 },
{ label: "管控状态", prop: "zdrBkZt", showOverflowTooltip: true, slotName: "zdrBkZt", width: 100 },
{ label: "审核状态", prop: "zdrZt", slotName: "zdrZt", width: 100 },
{ label: "入库时间", prop: "zdrRkkssj", },
{ label: "操作", prop: "controls", slotName: "controls", width: 280 },
]
});
@ -169,7 +170,7 @@ onMounted(() => {
addEdit('x', {
id: route.query.id
})
}else{
} else {
getList();
}
});
@ -195,7 +196,7 @@ const getList = () => {
pageData.tableConfiger.loading = true;
// 人员类型D_ZDRY_RYLX(01 重点 02 普通〉
// rylx: '01',
let data = {...pageData.pageConfiger, ...queryFrom.value,rylx: '03' };
let data = { ...pageData.pageConfiger, ...queryFrom.value, rylx: '03' };
qcckGet(data, "/mosty-gsxt/tbGsxtZdry/selectPage").then((res) => {
pageData.tableData = res.records || [];
pageData.total = res.total;
@ -224,6 +225,14 @@ const handleremove = (id) => {
})
}
const handleZdr = (id) => {
proxy.$confirm("确定要移至重点人?", "警告", { type: "warning" }).then(() => {
qcckPost({ id, rylx: '01' }, "/mosty-gsxt/tbGsxtZdry/rylxyd").then(() => {
proxy.$message({ type: "success", message: "移除成功" });
getList();
});
})
}
const chooseData = (data) => {
ids.value = Array.isArray(data) ? data.map((item) => item.id) : [];

View File

@ -4,9 +4,16 @@
<span class="title">{{ title }}重点人管理</span>
<div>
<el-button type="primary" size="small" v-if="butShow" :loading="loading" @click="submit">保存</el-button>
<el-button type="primary" size="small" v-if="showAuditShBtn && showButData"
@click="openAuditDialog('sh')">审核</el-button>
<el-button type="primary" size="small" v-if="showAuditSpBtn && showButData"
@click="openAuditDialog('sp')">审批</el-button>
<el-button size="small" @click="close">关闭</el-button>
</div>
</div>
<AuditDialog v-model="auditDialogVisible" :title="auditTitle" :formData="auditForm" :rules="auditRules"
:reasonProp="auditReasonProp" :loading="auditLoading" :dictEnum="D_BZ_SF" @cancel="handleAuditCancel"
@submit="handleAuditSubmit" />
<div class="form_cnt flex just-between">
<div class="left_box">
<ul class="anchor-list">
@ -20,8 +27,8 @@
:class="activeSection === 'character-section' ? 'active' : ''">背景信息</li>
<li @click="scrollToSection('controlInfo-section')"
:class="activeSection === 'controlInfo-section' ? 'active' : ''" v-if="!butShow">管控信息</li>
<li @click="scrollToSection('featinfo-section')"
:class="activeSection === 'featinfo-section' ? 'active' : ''" v-if="!butShow">全要素布控</li>
<li @click="scrollToSection('featinfo-section')" :class="activeSection === 'featinfo-section' ? 'active' : ''"
v-if="!butShow">全要素布控</li>
<li @click="scrollToSection('demandsInfo-section')"
:class="activeSection === 'demandsInfo-section' ? 'active' : ''" v-if="!butShow">密切联系人</li>
<li @click="scrollToSection('requestInfo-section')"
@ -69,13 +76,13 @@
<div id="judgmentRecord-section" v-if="!butShow">
<VisitRecord ref="visitRecord" :disabled="disabled" :showBut="showBut" :dataList="listQuery" />
</div>
<div id="historyAssembly-section" v-if="!butShow" >
<div id="historyAssembly-section" v-if="!butShow">
<CaseInfo ref="caseInfo" :disabled="disabled" :showBut="showBut" :dataList="listQuery" />
</div>
<div id="joblogging-section" v-if="!butShow">
<ActualPerformance ref="actualPerformance" :disabled="disabled" :showBut="showBut" :dataList="listQuery" />
</div>
<div id="joblogging-joblog" v-if="!butShow" >
<div id="joblogging-joblog" v-if="!butShow">
<CzModel ref="czModel" :disabled="disabled" :showBut="showBut" :dataList="listQuery" />
</div>
</div>
@ -87,6 +94,7 @@
import { getItem } from "@/utils/storage";
import { qcckGet, qcckPost, qcckPut } from "@/api/qcckApi.js";
import { tbGsxtZdrySelectVoById, tbGsxtZdrySave } from "@/api/zdr.js";
import AuditDialog from "./auditDialog.vue";
import ControlInfo from '../../mpvGroup/model/controlInfo.vue'
import Info from "../model/info.vue";
import PersonnelTags from '../model/personnelTags.vue'
@ -101,10 +109,11 @@ import CaseInfo from '../model/caseInfo.vue'
import ActualPerformance from '../model/actualPerformance.vue'
import CzModel from '../model/czModel.vue'
import { useRouter, useRoute } from 'vue-router'
import { ref, onUnmounted, getCurrentInstance } from "vue";
import { ref, onUnmounted, getCurrentInstance, computed, reactive } from "vue";
const router = useRouter()
const route = useRoute()
const { proxy } = getCurrentInstance();
const { D_BZ_SF } = proxy.$dict("D_BZ_SF");
const emit = defineEmits(["updateDate"]);
const chooseMarksVisible = ref(false);
const dialogForm = ref(false); //弹窗
@ -115,23 +124,50 @@ const listQuery = ref({});
const butShow = ref(false)
const title = ref('新增')
const showData = ref(false)
const modeType = ref("detail")
const currentRowId = ref("")
const auditDialogVisible = ref(false)
const auditLoading = ref(false)
const auditForm = reactive({
id: "",
sftg: undefined,
shBtgyy: "",
spBtgyy: ""
})
const auditRules = reactive({
sftg: [{ required: true, message: "请选择是否通过", trigger: "change" }],
shBtgyy: [{ required: true, message: "请输入不通过原因", trigger: "blur" }],
spBtgyy: [{ required: true, message: "请输入不通过原因", trigger: "blur" }]
})
const auditTitle = computed(() => modeType.value === "sp" ? "审批" : "审核")
const auditReasonProp = computed(() => modeType.value === "sp" ? "spBtgyy" : "shBtgyy")
const showAuditShBtn = computed(() => modeType.value === "detail" && dataListQuery.value?.zdrZt == "02")
const showAuditSpBtn = computed(() => modeType.value === "detail" && dataListQuery.value?.zdrZt == "04")
const props = defineProps({
rylx: {
type: String,
default: '01'
}
},
showButData: {
type: Boolean,
default: false
},
})
const dataListQuery = ref({})
// 初始化数据
const init = (type, row) => {
dialogForm.value = true;
modeType.value = type;
currentRowId.value = row?.id || "";
if (type == 'add') {
butShow.value = true
title.value = '新增'
disabled.value = false
showBut.value = false
listQuery.value = {}
} else {
butShow.value = false
dataListQuery.value = { ...row }
tbGsxtZdrySelectVoById({ id: row.id }).then(res => {
listQuery.value = { ...res }
})
@ -143,8 +179,7 @@ const init = (type, row) => {
disabled.value = true
showBut.value = false
title.value = '删除'
}
else {
} else {
disabled.value = true
showBut.value = false
title.value = '详情'
@ -213,7 +248,7 @@ const submit = async () => {
info.value.throwData()
// personnelTags.value.throwData(),
]);
tbGsxtZdrySave({...infoData,rylx:props.rylx}).then(res => {
tbGsxtZdrySave({ ...infoData, rylx: props.rylx }).then(res => {
proxy.$message({
message: '新增成功',
type: 'success',
@ -224,6 +259,43 @@ const submit = async () => {
};
const butzt = ref()
const openAuditDialog = (type) => {
// modeType.value = type;
butzt.value = type
auditForm.id = currentRowId.value || listQuery.value?.id || "";
auditForm.sftg = undefined;
auditForm.shBtgyy = "";
auditForm.spBtgyy = "";
auditDialogVisible.value = true;
}
const handleAuditCancel = () => {
auditDialogVisible.value = false;
auditLoading.value = false;
// modeType.value = "detail";
auditForm.id = "";
auditForm.sftg = undefined;
auditForm.shBtgyy = "";
auditForm.spBtgyy = "";
}
const handleAuditSubmit = () => {
auditLoading.value = true;
const url = butzt.value === "sp" ? "/mosty-gsxt/tbGsxtZdry/updateSp" : "/mosty-gsxt/tbGsxtZdry/updateSh";
const successMsg = modeType.value === "sp" ? "审批成功" : "审核成功";
qcckPost(auditForm, url).then(() => {
proxy.$message({
message: successMsg,
type: 'success',
})
auditDialogVisible.value = false;
modeType.value = "detail";
close();
}).catch(() => {
auditLoading.value = false;
});
}
// 关闭
const close = () => {
@ -235,6 +307,14 @@ const close = () => {
dialogForm.value = false;
loading.value = false;
auditLoading.value = false;
auditDialogVisible.value = false;
modeType.value = "detail";
currentRowId.value = "";
auditForm.id = "";
auditForm.sftg = undefined;
auditForm.shBtgyy = "";
auditForm.spBtgyy = "";
emit('updateDate')
};

View File

@ -0,0 +1,75 @@
<template>
<el-dialog :model-value="modelValue" :title="title" width="500px" @update:model-value="handleModelValueChange"
@close="handleCancel">
<el-form :model="formData" ref="elAuditForm" label-width="100px" :rules="rules" style="width: 100%;">
<el-form-item label="是否通过" prop="sftg" class="mt10 mb10">
<MOSTY.Select filterable v-model="formData.sftg" :dictEnum="dictEnum" style="width: 100%;" clearable
placeholder="请选择是否通过" />
</el-form-item>
<el-form-item label="不通过原因" :prop="reasonProp" v-if="formData.sftg == 0">
<MOSTY.Other style="width: 100%;" clearable v-model="formData[reasonProp]" type="textarea"
placeholder="请输入不通过原因" />
</el-form-item>
</el-form>
<template #footer>
<div class="flex just-center">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit" v-loading="loading">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import * as MOSTY from "@/components/MyComponents/index";
import { ref } from "vue";
defineProps({
modelValue: {
type: Boolean,
default: false
},
title: {
type: String,
default: "审核"
},
formData: {
type: Object,
default: () => ({})
},
rules: {
type: Object,
default: () => ({})
},
reasonProp: {
type: String,
default: "shBtgyy"
},
loading: {
type: Boolean,
default: false
},
dictEnum: {
type: Array,
default: () => []
}
});
const emit = defineEmits(["update:modelValue", "cancel", "submit"]);
const elAuditForm = ref();
const handleModelValueChange = (val) => {
emit("update:modelValue", val);
};
const handleCancel = () => {
emit("cancel");
};
const handleSubmit = () => {
elAuditForm.value.validate((valid) => {
if (!valid) return;
emit("submit");
});
};
</script>

View File

@ -2,7 +2,7 @@
<div>
<!-- 搜索 -->
<div ref="searchBox" class="mt10">
<Search :searchArr="searchConfiger" @submit="onSearch" >
<Search :searchArr="searchConfiger" @submit="onSearch">
<el-button type="primary" size="small" @click="addEdit('add', '')">
<el-icon style="vertical-align: middle">
<CirclePlus />
@ -13,10 +13,9 @@
</div>
<!-- 表格 -->
<div class="margTop">
<MyTable :tableData="pageData.tableData" :tableColumn="pageData.tableColumn" :tableHeight="pageData.tableHeight"
:key="pageData.keyCount" :tableConfiger="pageData.tableConfiger" :controlsWidth="pageData.controlsWidth"
@chooseData="chooseData">
<div class="margTop" style="padding: 0;">
<WarnDataTable :data="pageData.tableData" :columns="pageData.tableColumn" :tableHeight="pageData.tableHeight"
:loading="pageData.tableConfiger.loading" @selection-change="chooseData">
<template #bqList="{ row }">
<ul>
<li class="one_text_detail marks mb4" :key="index" v-for="(item, index) in row.bqList">{{ item.bqMc }}({{
@ -39,7 +38,7 @@
<DictTag :tag="false" :value="row.zdrRyjb" :options="D_GS_ZDR_RYJB" />
</template>
<template #zdrBkZt="{ row }">
<DictTag :tag="false" :value="row.zdrBkZt" :options="D_ZDRGK_GKZT" />
<DictTag :tag="false" :value="row.zdrBkZt" :options="D_BZ_RCBKZT" />
</template>
<template #zdrCzzt="{ row }">
<DictTag :tag="false" :value="row.zdrCzzt" :options="D_GS_ZDR_CZZT" />
@ -54,7 +53,9 @@
<!-- 操作 -->
<template #controls="{ row }">
<el-link size="small" type="success" @click="handleremove(row.id)"></el-link>
<!-- <el-link size="small" type="success" @click="handleremove(row.id)">至关注库</el-link> -->
<el-link size="small" type="success" @click="handleMoveToFocus(row.id)">移入关注库</el-link>
<el-link size="small" type="success" @click="handleremove(row.id)">移入基础库</el-link>
<el-link size="small" type="success" v-if="row.zdrZt == '01' || row.zdrZt == '03'"
@click="handleSend(row.id)">送审</el-link>
<el-link size="small" type="primary" v-if="row.zdrZt == '01' || row.zdrZt == '03'"
@ -62,7 +63,7 @@
<el-link size="small" type="primary" @click="addEdit('detail', row)">详情</el-link>
<el-link size="small" type="danger" @click="deleteRow(row.id)">删除</el-link>
</template>
</MyTable>
</WarnDataTable>
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
...pageData.pageConfiger,
total: pageData.total
@ -84,7 +85,7 @@ import { ElMessage } from "element-plus";
import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
import ZxsForm from "./components/zxsForm.vue";
import PageTitle from "@/components/aboutTable/PageTitle.vue";
import MyTable from "@/components/aboutTable/MyTable.vue";
import WarnDataTable from "@/views/backOfficeSystem/ces/components/WarnDataTable.vue";
import Pages from "@/components/aboutTable/Pages.vue";
import Search from "@/components/aboutTable/Search.vue";
import AddForm from "./components/addForm.vue";
@ -95,8 +96,8 @@ import { getItem } from "@/utils/storage.js";
const router = useRouter()
const route = useRoute()
const { proxy } = getCurrentInstance();
const { D_GS_ZDQT_ZT, D_GS_ZDR_RYJB, D_BZ_XB, D_BZ_MZ, D_BZ_XZQHDM, D_ZDRGK_GKZT, D_GS_ZDR_CZZT, D_GS_BQ_ZL, D_GS_BQ_LB, D_GS_BQ_LX, D_GS_ZDR_YJDJ, D_GS_BK_SSJZ, D_GS_BK_SQLX, D_BZ_SF, D_GS_XS_LY, D_BZ_SSZT, D_GS_XS_LX, D_GS_XS_QTLX } =
proxy.$dict("D_GS_ZDQT_ZT", "D_GS_ZDR_RYJB", "D_BZ_XB", "D_BZ_MZ", "D_BZ_XZQHDM", "D_ZDRGK_GKZT", "D_GS_ZDR_CZZT", "D_GS_BQ_ZL", "D_GS_BQ_LB", "D_GS_BQ_LX", "D_GS_ZDR_YJDJ", "D_GS_BK_SSJZ", "D_GS_BK_SQLX", "D_BZ_SF", "D_GS_XS_LY", "D_BZ_SSZT", "D_GS_XS_LX", "D_GS_XS_QTLX");
const { D_GS_ZDQT_ZT, D_GS_ZDR_RYJB, D_BZ_XB, D_BZ_MZ, D_BZ_RCBKZT, D_BZ_XZQHDM, D_ZDRGK_GKZT, D_GS_ZDR_CZZT, D_GS_BQ_ZL, D_GS_BQ_LB, D_GS_BQ_LX, D_GS_ZDR_YJDJ, D_GS_BK_SSJZ, D_GS_BK_SQLX, D_BZ_SF, D_GS_XS_LY, D_BZ_SSZT, D_GS_XS_LX, D_GS_XS_QTLX } =
proxy.$dict("D_GS_ZDQT_ZT", "D_BZ_RCBKZT", "D_GS_ZDR_RYJB", "D_BZ_XB", "D_BZ_MZ", "D_BZ_XZQHDM", "D_ZDRGK_GKZT", "D_GS_ZDR_CZZT", "D_GS_BQ_ZL", "D_GS_BQ_LB", "D_GS_BQ_LX", "D_GS_ZDR_YJDJ", "D_GS_BK_SSJZ", "D_GS_BK_SQLX", "D_BZ_SF", "D_GS_XS_LY", "D_BZ_SSZT", "D_GS_XS_LX", "D_GS_XS_QTLX");
const obj = ref({});
const showzxs = ref(false);
const zxsDilof = ref();
@ -151,19 +152,20 @@ const pageData = reactive({
},
controlsWidth: 250,
tableColumn: [
{ label: "姓名", prop: "ryXm",width: 100 },
{ label: "性别", prop: "ryXb", showSolt: true, width: 80 },
{ label: "姓名", prop: "ryXm", width: 100 },
{ label: "性别", prop: "ryXb", slotName: "ryXb", width: 80 },
{ label: "身份证", prop: "rySfzh", width: 170 },
{ label: "民族", prop: "ryMz", showSolt: true, width: 80 },
{ label: "民族", prop: "ryMz", slotName: "ryMz", width: 80 },
{ label: "户籍派出所", prop: "hjdPcsmc" },
{ label: "标签", prop: "bqList", showSolt: true, showOverflowTooltip: true },
{ label: "管单位", prop: "gxSsbmmc" },
{ label: "管控状态", prop: "zdrBkZt", showOverflowTooltip: true,showSolt: true, width: 100 },
{ label: "审核状态", prop: "zdrZt", showSolt: true, width: 100 },
{ label: "标签", prop: "bqList", slotName: "bqList", showOverflowTooltip: true },
{ label: "管单位", prop: "gxSsbmmc" },
{ label: "管控状态", prop: "zdrBkZt", showOverflowTooltip: true, slotName: "zdrBkZt", width: 100 },
{ label: "审核状态", prop: "zdrZt", slotName: "zdrZt", width: 100 },
{ label: "入库时间", prop: "zdrRkkssj", },
{ label: "操作", prop: "controls", slotName: "controls", width: 250 },
]
});
const isShiQzDelet = ref(false)
const isShiQzDelet = ref(false)
onMounted(() => {
tabHeightFn();
const isShiQz = getItem('roleList').find(item => item.roleCode == 'JS_777777') != undefined
@ -172,7 +174,7 @@ onMounted(() => {
addEdit('x', {
id: route.query.id
})
}else{
} else {
getList();
}
});
@ -198,7 +200,7 @@ const getList = () => {
pageData.tableConfiger.loading = true;
// 人员类型D_ZDRY_RYLX(01 重点 02 普通〉
// rylx: '01',
let data = {...pageData.pageConfiger, ...queryFrom.value,rylx: '01' };
let data = { ...pageData.pageConfiger, ...queryFrom.value, rylx: '01' };
qcckGet(data, "/mosty-gsxt/tbGsxtZdry/selectPage").then((res) => {
pageData.tableData = res.records || [];
pageData.total = res.total;
@ -226,7 +228,14 @@ const handleremove = (id) => {
});
})
}
const handleMoveToFocus = (id) => {
proxy.$confirm("确定要移至关注库?", "警告", { type: "warning" }).then(() => {
qcckPost({ id, rylx: '03' }, "/mosty-gsxt/tbGsxtZdry/rylxyd").then(() => {
proxy.$message({ type: "success", message: "移除成功" });
getList();
});
})
}
const chooseData = (data) => {
ids.value = Array.isArray(data) ? data.map((item) => item.id) : [];

View File

@ -6,38 +6,26 @@
<el-button type="primary" v-if="showBut" :disabled="disabled" @click="submit">保存</el-button>
</div>
<div>
<FormMessage :disabled="disabled" v-model="listQuery" :formList="formData" labelWidth="100px" ref="elform"
<FormMessage :disabled="disabled" v-model="listQuery" :formList="formData" labelWidth="130px" ref="elform"
:rules="rules">
<template #ryzp>
<div style="width: 100%; padding-left: 50px">
<MOSTY.Upload :showBtn="false" :limit="1" v-model="listQuery.ryzp" />
</div>
</template>
<template #ryLxdh>
<div class="phone-input-container">
<div class="inputGroup" v-for="(item, index) in listQuery.ryLxdh" :key="index">
<el-input v-model="listQuery.ryLxdh[index]" class="group" placeholder="请输入电话号码" />
<div class="flex align-center but">
<el-button type="primary" :icon="Plus" circle @click="addPhone" title="添加电话号码"
v-if="listQuery.ryLxdh.length - 1 == index" />
<el-button type="success" :icon="Minus" circle @click="removePhone(index)" title="删除电话号码" />
</div>
</div>
</div>
</template>
<template #gkmjxm>
<template #gkMjXm>
<div>
<el-input v-model="listQuery.gkmjxm" class="group" placeholder="请输入管控民警姓名" readonly
<el-input v-model="listQuery.gkMjXm" class="group" placeholder="请输入管控民警姓名" readonly
@click="chooseMarksVisible = true" />
</div>
</template>
<template #gkmjsfzh>
<!-- <template #gkmjsfzh>
<div>
<el-input v-model="listQuery.gkmjsfzh" class="group" placeholder="请输入管控民警身份证号" readonly
@click="chooseMarksVisible = true" />
</div>
</template>
<!-- { label: "管控民警", prop: "gkmjxm", type: "slot" }, -->
</template> -->
<!-- { label: "管控民警", prop: "gkMjXm", type: "slot" }, -->
<!-- { label: "管控民警身份证号", prop: "gkmjsfzh", type: "slot" }, -->
</FormMessage>
</div>
@ -48,8 +36,8 @@
<script setup>
import * as rule from "@/utils/rules.js";
import { IdCard } from "@/utils/validate.js";
import * as MOSTY from "@/components/MyComponents/index";
import { Plus, Minus } from "@element-plus/icons-vue";
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
import { ref, reactive, onMounted, getCurrentInstance, watch } from "vue";
@ -73,57 +61,61 @@ const props = defineProps({
const rules = reactive({
ryXm: [{ required: true, message: "请输入姓名", trigger: "blur" }],
...rule.identityCardRule({ validator: true }, 'rySfzh'), //身份证校验
...rule.phoneRule({ validator: true }, "ryLxdh"), // 是否必填 是否进行校验,
...rule.phoneRule({ validator: true }, "gkMjLxfs"), // 是否必填 是否进行校验,
rySfzh: [{ required: true, message: "请输入身份证号", trigger: "blur" }],
ryLxdh: [{ required: true, message: "请输入联系电话", trigger: "blur" }],
ryXb: [{ required: true, message: "请选择性别", trigger: "change" }],
ryMz: [{ required: true, message: "请选择民族", trigger: "change" }],
ryCsrq: [{ required: true, message: "请选择出生日期", trigger: "change" }],
zdrRyjb: [{ required: true, message: "请选择人员级别", trigger: "change" }],
zdrYjdj: [{ required: true, message: "请选择预警等级", trigger: "change" }],
zrSsbmdm: [{ required: true, message: "请选择责任单位", trigger: "change" }],
// gkmjxm: [{ required: true, message: "请选择管控民警", trigger: "change" }],
gxSsbmdm: [{ required: true, message: "请选择协管单位", trigger: "change" }],
zdrRkkssj: [{ required: true, message: "请选择入库开始时间", trigger: "change" }],
zdrRkjssj: [{ required: true, message: "请选择入库结束时间", trigger: "change" }],
gkMjLxfs: [{ required: true, message: "请输入民警联系方式", trigger: "blur" }],
// gkMjXm: [{ required: true, message: "请选择管控民警", trigger: "change" }],
// gkmjsfzh: [{ required: true, message: "请选择管控民警身份证号", trigger: "change" }],
// rylx: [{ required: true, message: "请选择人员类型", trigger: "change" }]
});
const listQuery = ref({ ryLxdh: [""] }); //表单
const listQuery = ref({}); //表单
const chooseMarksVisible = ref(false); // 控制标签选择弹窗显示
const roleIds = ref([]); // 已选择的标签ID
const formData = ref([
{ label: "人员照片", prop: "ryzp", type: "slot", width: "100%" },
{ label: "姓名", prop: "ryXm", type: "input" },
{ label: "性别", prop: "ryXb", type: "select", options: D_BZ_XB },
{ label: "身份证号", prop: "rySfzh", type: "input" },
{ label: "籍贯", prop: "ryJg", type: "select", options: D_BZ_XZQHDM },
{ label: "曾用名", prop: "cym", type: "input" },
{ label: "文化程度", prop: "whcdBm", type: "select", options: D_BZ_WHCD },
{ label: "民族", prop: "ryMz", type: "select", options: D_BZ_MZ },
{ label: "政治面貌", prop: "zzmm", type: "select", options: D_BZ_ZZMM },
{ label: "职业", prop: "zyBm", type: "select", options: D_ZDRY_ZYLB },
{ label: "人员级别", prop: "zdrRyjb", type: "select", options: D_GS_ZDR_RYJB },
{ label: "预警等级", prop: "zdrYjdj", type: "select", options: D_GS_ZDR_YJDJ },
{ label: "出生日期", prop: "ryCsrq", type: "date" },
{ label: "户籍地区划", prop: "hjdQh", type: "select", options: D_BZ_XZQHDM },
{ label: "户籍地详址", prop: "hjdXz", type: "input" },
{ label: "户籍地派出所", prop: "hjdPcsdm", depMc: "hjdPcsmc", type: "department" },
{ label: "现住地区划", prop: "xzdQh", type: "select", options: D_BZ_XZQHDM },
{ label: "现住地详址", prop: "xzdXz", type: "input" },
{ label: "现住地派出所", prop: "xzdPcsdm", depMc: "xzdPcsmc", type: "department" },
{ label: "管控民警", prop: "gkmjxm", type: "slot" },
{ label: "民警身份证", prop: "gkmjsfzh", type: "slot" },
{ label: "管辖单位", prop: "gxSsbmdm", depMc: 'gxSsbmmc', type: "department" },
{ label: "诉求单位", prop: "sqSsbmdm", depMc: 'sqSsbmmc', type: "department" },
{ label: "责任单位", prop: "zrSsbmdm", depMc: 'zrSsbmmc', type: "department" },
{ label: "所属警种", prop: "zdrSsjz", type: "select", options: D_GS_BK_SSJZ },
{ label: "涉及警种", prop: "zdrSjjz", type: "select", options: D_GS_BK_SSJZ, multiple: true },
{ label: "婚姻状态", prop: "hyzk", type: "select", options: D_BZ_HYZK },
// { label: "处置状态", prop: "zdrCzzt", type: "select", options: D_GS_ZDR_CZZT },
// { label: "布控状态", prop: "zdrBkZt", type: "select", options: D_BZ_RCBKZT },
{ label: "姓名", prop: "ryXm", type: "input", width: "30%" },
{ label: "身份证号", prop: "rySfzh", type: "input", width: "30%" },
{ label: "性别", prop: "ryXb", type: "select", options: D_BZ_XB, width: "30%" },
{ label: "出生日期", prop: "ryCsrq", type: "date", width: "30%" },
{ label: "民族", prop: "ryMz", type: "select", options: D_BZ_MZ, width: "30%" },
{ label: "协管单位", prop: "gxSsbmdm", depMc: 'gxSsbmmc', type: "department", width: "30%" },
{ label: "预警等级", prop: "zdrYjdj", type: "select", options: D_GS_ZDR_YJDJ, width: "30%" },
{ label: "管控民警", prop: "gkMjXm", type: "slot", width: "30%" },
{ label: "民警联系方式", prop: "gkMjLxfs", type: "input", width: "30%" },
{ label: "入库开始时间", prop: "zdrRkkssj", type: "datetime", width: "30%" },
{ label: "入库结束时间", prop: "zdrRkjssj", type: "datetime", width: "30%" },
{ label: "重点人联系电话", prop: "ryLxdh", type: "input", width: "30%" },
{ label: "籍贯", prop: "ryJg", type: "input", width: "30%" },
{ label: "曾用名", prop: "cym", type: "input", width: "30%" },
{ label: "文化程度", prop: "whcdBm", type: "select", options: D_BZ_WHCD, width: "30%" },
{ label: "政治面貌", prop: "zzmm", type: "select", options: D_BZ_ZZMM, width: "30%" },
{ label: "职业", prop: "zyBm", type: "input", width: "30%" },
{ label: "人员级别", prop: "zdrRyjb", type: "select", options: D_GS_ZDR_RYJB, width: "30%" },
{ label: "户籍地区划", prop: "hjdQh", type: "input", width: "30%" },
{ label: "户籍地详址", prop: "hjdXz", type: "input", width: "30%" },
{ label: "户籍地派出所", prop: "hjdPcsmc", type: "input", width: "30%" },
{ label: "现住地区划", prop: "xzdQh", type: "input", width: "30%" },
{ label: "现住地详址", prop: "xzdXz", type: "input", width: "30%" },
{ label: "现住地派出所", prop: "xzdPcsdm", depMc: "xzdPcsmc", type: "department", width: "30%" },
// { label: "民警身份证", prop: "gkmjsfzh", type: "slot" },
{ label: "诉求单位", prop: "sqSsbmmc", type: "input", width: "30%" },
{ label: "责任单位", prop: "zrSsbmmc", type: "input", width: "30%" },
{ label: "所属警种", prop: "zdrSsjz", type: "select", options: D_GS_BK_SSJZ, width: "30%" },
{ label: "涉及警种", prop: "zdrSjjz", type: "select", options: D_GS_BK_SSJZ, multiple: true, width: "30%" },
{ label: "婚姻状态", prop: "hyzk", type: "select", options: D_BZ_HYZK, width: "30%" },
{ label: "处置状态", prop: "zdrCzzt", type: "select", options: D_GS_ZDR_CZZT, width: "30%" },
{ label: "布控状态", prop: "zdrBkZt", type: "select", options: D_BZ_RCBKZT, width: "30%" },
// { label: "人员类型", prop: "rylx", type: "select", options: D_ZDRY_RYLX },
{ label: "入库开始时间", prop: "zdrRkkssj", type: "datetime" },
{ label: "入库结束时间", prop: "zdrRkjssj", type: "datetime" },
{ label: "Mac地址", prop: "macDz", type: "input" },
{ label: "联系电话", prop: "ryLxdh", type: "slot", width: "100%" },
{ label: "Mac地址", prop: "macDz", type: "input", width: "30%" },
// { label: "标签选择", prop: "tags", type: "slot", width: "100%" },
{ label: "管控原因", prop: "zdrLkyy", type: "textarea", width: "100%" },
]);
@ -150,9 +142,22 @@ const deepClone = (obj) => {
}
return clonedObj;
};
// 监听身份证号变化,自动填充性别、出生日期和民族
watch(() => listQuery.value.rySfzh, (val) => {
if (val && val.length === 18) {
// 使用IdCard方法提取出生日期
listQuery.value.ryCsrq = IdCard(val, 1);
// 使用IdCard方法提取性别
const genderText = IdCard(val, 2);
listQuery.value.ryXb = D_BZ_XB.value.find(item => item.zdmc === genderText).dm;
}
});
// 监听props.dataList变化处理初始化数据
watch(() => props.dataList, (val) => {
if (val) {
console.log(val);
// 使用深拷贝避免直接引用同一个对象
listQuery.value = deepClone(val);
// 处理照片数据
@ -175,14 +180,9 @@ const submit = () => {
};
//
const gettbGsxtZdryUpdate = () => {
const promes = {
...listQuery.value,
ryzp: listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : "",
ryLxdh: listQuery.value.ryLxdh,
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz),
}
@ -200,28 +200,6 @@ const gettbGsxtZdryUpdate = () => {
});
})
}
// 添加电话号码
const addPhone = () => {
// 确保新添加的电话号码与现有数据结构一致
// 创建深拷贝以避免响应式更新问题
const newPhoneList = [...listQuery.value.ryLxdh];
newPhoneList.push('');
// 用全新数组替换现有数组确保Vue正确检测到变化
listQuery.value.ryLxdh = newPhoneList;
}
// 删除电话号码
const removePhone = (index) => {
if (listQuery.value.ryLxdh.length > 1) {
listQuery.value.ryLxdh.splice(index, 1);
} else {
// 清空输入但保留输入框
listQuery.value.ryLxdh[0] = '';
proxy.$message.warning('至少保留一个联系电话');
}
}
const throwData = () => {
@ -229,32 +207,19 @@ const throwData = () => {
if (elform.value && elform.value.validate) {
elform.value.submit((data) => {
// 过滤掉空的电话号码
const validPhones = listQuery.value.ryLxdh.filter(phone => phone && phone.trim());
if (validPhones.length === 0) {
proxy.$message.warning('请至少输入一个有效的联系电话');
reject(new Error('请至少输入一个有效的联系电话'));
return;
}
resolve({
...listQuery.value,
ryzp: listQuery.value.ryzp && listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : '',
ryLxdh: validPhones,
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz),
});
})
} else {
elform.value.submit((data) => {
// 如果没有验证方法,直接返回数据
const validPhones = listQuery.value.ryLxdh.filter(phone => phone && phone.trim());
if (validPhones.length === 0) {
proxy.$message.warning('请至少输入一个有效的联系电话');
reject(new Error('请至少输入一个有效的联系电话'));
return;
}
resolve({
...listQuery.value,
ryzp: listQuery.value.ryzp && listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : '',
ryLxdh: validPhones,
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz),
});
@ -265,8 +230,9 @@ const throwData = () => {
};
const choosed = (val) => {
roleIds.value = [val[0].id]
listQuery.value.gkmjxm = val[0].userName
listQuery.value.gkMjXm = val[0].userName
listQuery.value.gkmjsfzh = val[0].idEntityCard
listQuery.value.gkMjLxfs = val[0].mobile
console.log(listQuery.value);
};

View File

@ -96,48 +96,6 @@
<!-- 操作 -->
<template #controls="{ row }">
<el-popover placement="left" :visible="row.visible" :width="400" trigger="manual">
<template #reference>
<el-link size="small" type="warning" v-if="row.zdrZt == '02'"
@click="row.visible = !row.visible, chooseRow.id = row.id">审核</el-link>
</template>
<el-form :model="chooseRow" ref="elRowForm" :inline="true" label-width="100px" :rules="rules">
<el-form-item label="是否通过" prop="sftg" class="mt10 mb10" style="width: 100%;">
<MOSTY.Select filterable v-model="chooseRow.sftg" :dictEnum="D_BZ_SF" width="100%" clearable
placeholder="请选择是否通过" />
</el-form-item>
<el-form-item label="不通过原因" prop="shBtgyy" v-if="chooseRow.sftg == 0" style="width: 100%;">
<MOSTY.Other style="width: 100%;" clearable v-model="chooseRow.shBtgyy" type="textarea"
placeholder="请输入不通过原因" />
</el-form-item>
</el-form>
<div class="flex just-center mt10">
<el-button @click.stop="cancelRow(row)">取消</el-button>
<el-button type="primary" @click.stop="handleSend(row)" v-loading="btnloading">确定</el-button>
</div>
</el-popover>
<el-popover placement="left" :visible="row.visible1" :width="400" trigger="manual">
<template #reference>
<el-link size="small" type="primary" v-if="row.zdrZt == '04'"
@click="row.visible1 = !row.visible1, chooseRow.id = row.id">审批</el-link>
</template>
<el-form :model="chooseRow" ref="elRowForm1" :inline="true" label-width="100px" :rules="rules">
<el-form-item label="是否通过" prop="sftg" class="mt10 mb10" style="width: 100%;">
<MOSTY.Select filterable v-model="chooseRow.sftg" :dictEnum="D_BZ_SF" width="100%" clearable
placeholder="请选择是否通过" />
</el-form-item>
<el-form-item label="不通过原因" prop="spBtgyy" v-if="chooseRow.sftg == 0" style="width: 100%;">
<MOSTY.Other style="width: 100%;" clearable v-model="chooseRow.spBtgyy" type="textarea"
placeholder="请输入不通过原因" />
</el-form-item>
</el-form>
<div class="flex just-center mt10">
<el-button @click.stop="cancelRowSp(row)">取消</el-button>
<el-button type="primary" @click.stop="handleSendSp(row)" v-loading="btnloading">确定</el-button>
</div>
</el-popover>
<el-link size="small" type="primary" @click="addEdit('detail', row)">详情</el-link>
</template>
</MyTable>
@ -147,19 +105,18 @@
}"></Pages>
</div>
<!-- 详情 -->
<AddForm ref="addFormDiloag" @updateDate="getList"
<AddForm ref="addFormDiloag" @updateDate="getList" :showButData="true"
:dic="{ D_GS_ZDR_RYJB, D_BZ_XB, D_BZ_MZ, D_BZ_XZQHDM, D_GS_ZDR_BK_ZT, D_GS_ZDR_CZZT, D_GS_BQ_ZL, D_GS_BQ_LB, D_GS_BQ_LX, D_GS_ZDR_YJDJ, D_GS_BK_SSJZ }" />
<!-- 选择用户 -->
<ChooseUser v-model="chooseUserVisible" @choosedUsers="handleUserSelected" :roleIds="roleIds" />
<!-- 转线索 -->
<ZxsForm v-if="showzxs" ref="zxsDilof" @change="getList"
:dic="{ D_BZ_SF, D_BZ_XB, D_GS_XS_LY, D_BZ_SSZT, D_GS_XS_LX, D_GS_XS_QTLX }"></ZxsForm>
<Ypdolog v-model="showYpdolog" :dataList="ids"/>
<Ypdolog v-model="showYpdolog" :dataList="ids" />
</div>
</template>
<script setup>
import * as MOSTY from "@/components/MyComponents/index";
import { ElMessage } from "element-plus";
import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
import ZxsForm from "../mpvPeo/components/zxsForm.vue";
@ -184,15 +141,6 @@ const ids = ref([]);
const choosList = ref([]);
const visible = ref(false);
const visiblefp = ref(false);
const chooseRow = ref({})
const btnloading = ref(false)
const elRowForm = ref()
const elRowForm1 = ref()
const rules = reactive({
sftg: [{ required: true, message: "请选择是否通过", trigger: "change" }],
shBtgyy: [{ required: true, message: "请输入不通过原因", trigger: "blur" }],
spBtgyy: [{ required: true, message: "请输入不通过原因", trigger: "blur" }]
});
const searchConfiger = ref([
{
@ -346,50 +294,7 @@ const handleZxs = () => {
};
const cancelRow = (row) => {
row.visible = false;
chooseRow.value = {};
btnloading.value = false;
elRowForm.value.resetFields()
};
// 审核
const handleSend = (val) => {
elRowForm.value.validate((valid) => {
if (!valid) return;
btnloading.value = true;
qcckPost(chooseRow.value, "/mosty-gsxt/tbGsxtZdry/updateSh").then(() => {
proxy.$message({ type: "success", message: "审核成功" });
cancelRow(val)
getList();
}).catch(() => {
btnloading.value = false;
});
})
}
// 审批
const cancelRowSp = (row) => {
row.visible1 = false;
chooseRow.value = {};
btnloading.value = false;
elRowForm1.value.resetFields()
}
// 审批
const handleSendSp = (val) => {
elRowForm1.value.validate((valid) => {
if (!valid) return;
btnloading.value = true;
qcckPost(chooseRow.value, "/mosty-gsxt/tbGsxtZdry/updateSp").then(() => {
proxy.$message({ type: "success", message: "审批成功" });
cancelRowSp(val);
getList();
}).catch(() => {
btnloading.value = false;
});
})
}
const showYpdolog=ref(false)
const showYpdolog = ref(false)
//新增编辑
const addEdit = (type, row) => {

View File

@ -3,7 +3,8 @@
<div class="head_box">
<span class="title">{{ title }}临安码线索</span>
<div>
<el-button type="primary" v-if="!listQuery.gzlid&&title!=='详情'" :loading="loading" @click="submit">保存</el-button>
<el-button type="primary" v-if="!listQuery.gzlid && title !== '详情'" :loading="loading"
@click="submit">保存</el-button>
<el-button @click="close">关闭</el-button>
</div>
</div>
@ -11,7 +12,7 @@
<div class="flex">
<div class="form_cnt" :class="listQuery.gzlid&&title=='详情' ? 'ww80' : 'ww100'">
<div class="form_cnt" :class="listQuery.gzlid && title == '详情' ? 'ww80' : 'ww100'">
<FormMessage v-model="listQuery" :formList="formData" ref="elform" :rules="rules" :disabled="title == '详情'">
<template #gapdive>
<div style="width: 100%;height: 10px;" class="mb20">
@ -31,7 +32,8 @@
</template>
</FormMessage>
<el-divider content-position="left"><span class="mr20">相关人员</span>
<el-button type="primary" size="small" @click="addEdit('add', null)" :disabled="title == '详情'">添加人员</el-button>
<el-button type="primary" size="small" @click="addEdit('add', null)"
:disabled="title == '详情'">添加人员</el-button>
</el-divider>
<MyTable :tableData="pageForm.tableData" :tableColumn="pageForm.tableColumn" :tableHeight="pageForm.tableHeight"
:key="pageForm.keyCount" :tableConfiger="pageForm.tableConfiger" :controlsWidth="pageForm.controlsWidth">
@ -50,7 +52,7 @@
</template>
</MyTable>
</div>
<div v-if="listQuery.gzlid&&title=='详情'" class="ww20">
<div v-if="listQuery.gzlid && title == '详情'" class="ww20">
<ApprovalEcho ref="approvalEcho" />
</div>
</div>
@ -67,8 +69,11 @@ import AddPeo from '@/components/addPerson/index.vue'
import MyTable from "@/components/aboutTable/MyTable.vue";
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import { qcckGet, qcckPost, qcckPut } from "@/api/qcckApi.js";
import { ref, defineExpose, reactive, onMounted, defineEmits, getCurrentInstance, nextTick,watch } from "vue";
import { ref, defineExpose, reactive, onMounted, defineEmits, getCurrentInstance, nextTick, watch } from "vue";
import ApprovalEcho from "@/components/flowPath/ApprovalEcho.vue";
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
const emit = defineEmits(["change"]);
const props = defineProps({
dic: Object
@ -150,7 +155,7 @@ watch(() => approvalEcho.value, (val) => {
approvalEcho.value.getWorkflow(listQuery.value.gzlid)
}
},{deep:true})
}, { deep: true })
// 打开弹窗
const addEdit = (type, row, index) => {
@ -205,6 +210,11 @@ const close = () => {
listQuery.value = {};
dialogForm.value = false;
loading.value = false;
if (route.query.id) {
const query = { ...route.query };
delete query.id;
router.replace({ query });
}
};
// 表格高度计算

View File

@ -39,7 +39,7 @@
<el-link size="small" type="primary" @click.stop="createProcess(row)">审核</el-link>
<el-link size="small" type="danger" @click="delDictItem(row.id)">删除</el-link>
</template>
<el-link size="small" type="warning" @click="openXsxf( row)">线索下发</el-link>
<el-link size="small" type="warning" @click="openXsxf(row)">线索下发</el-link>
</template>
</MyTable>
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
@ -53,7 +53,7 @@
</div>
<SubmissionProcess v-model="showSp" :data="rowData"
:userData="{ ajmc: '线索数据采集审批', flowType: 'XSSJCJSP', modelName: '线索' }" :path="fixedValue" @getList="getList" />
<Xsxf v-model="xsxfShow" :dataList="rowData"/>
<Xsxf v-model="xsxfShow" :dataList="rowData" />
</template>
<script setup>
@ -66,10 +66,12 @@ import { qcckGet, qcckPost } from "@/api/qcckApi.js";
import { reactive, ref, onMounted, getCurrentInstance, nextTick } from "vue";
import { useRouter } from "vue-router";
import Xsxf from "./components/xsxf.vue";
import { useRoute } from 'vue-router'
const router = useRouter();
const route = useRoute()
const { proxy } = getCurrentInstance();
const { D_GS_XS_XSCZZT, D_GS_XS_LY, D_BZ_SSZT, D_BZ_SF, D_GS_XS_LX, D_GS_XS_QTLX, D_BZ_XB, D_BZ_XSSHZT } = proxy.$dict("D_GS_XS_XSCZZT", "D_GS_XS_LY", "D_BZ_SSZT", "D_BZ_SF", "D_GS_XS_LX", "D_GS_XS_QTLX", "D_BZ_XB", "D_BZ_XSSHZT"); //获取字典数据
const detailDiloag = ref();
const detailDiloag = ref(null);
const searchBox = ref(); //搜索框
const isShow = ref(false)
const searchConfiger = ref([
@ -109,6 +111,11 @@ const pageData = reactive({
const queryFrom = ref({});
onMounted(() => {
if (route.query.id) {
addEdit('detail', {
id: route.query.id
})
}
getList()
tabHeightFn();
});
@ -205,7 +212,7 @@ const createProcess = (row) => {
const xsxfShow = ref(false)
const openXsxf = (row) => {
xsxfShow.value = true
rowData.value = {...row}
rowData.value = { ...row }
}
</script>

View File

@ -2,7 +2,7 @@
<div>
<!-- 搜索 -->
<div ref="searchBox" class="mt10">
<Search :searchArr="searchConfiger" @submit="onSearch" :key="pageData.keyCount" >
<Search :searchArr="searchConfiger" @submit="onSearch" :key="pageData.keyCount">
<el-button type="primary" size="small" @click="getDataById('add', '')">
<el-icon style="vertical-align: middle">
<CirclePlus />
@ -51,7 +51,6 @@ import { reactive, ref, onMounted, getCurrentInstance, watch, computed } from "v
import AddForm from "./addForm.vue";
const { proxy } = getCurrentInstance();
const { D_BZ_YPFS, D_BZ_YPLX } = proxy.$dict("D_BZ_YPFS", "D_BZ_YPLX")
const detailDiloag = ref();
const searchBox = ref(); //搜索框
const userInfo = ref({})
@ -59,7 +58,7 @@ onMounted(() => {
userInfo.value = getItem('deptId') ? getItem('deptId')[0] : {}
tabHeightFn()
if (route.query.id) {
detailDiloag.value.init('edit', {
addForm.value.init('detail', {
id: route.query.id
});
return

View File

@ -12,9 +12,10 @@
<el-option label="人像预警" value="人像预警" />
<el-option label="车辆预警" value="车辆预警" />
<el-option label="区域预警" value="区域预警" />
<el-option label="布控预警" value="布控预警" />
</el-select>
</el-popover>
<el-popover placement="right" :width="240" style='height: 300px;' trigger="click" v-else-if ="item == '标签预警'">
<el-popover placement="right" :width="240" style='height: 300px;' trigger="click" v-else-if="item == '标签预警'">
<template #reference>
<el-button :type="BqbutStylChange(qh) ? 'primary' : 'default'" size="small">{{ item }}</el-button>
</template>
@ -62,7 +63,7 @@ import Cs from '@/views/backOfficeSystem/ces/index.vue'
import { onMounted, ref } from "vue";
// "人像预警", "车辆预警",, "区域预警","无人机预警"
const butList = ref(["七类重点", '政保预警', "布控预警", "预警整合"])
const qh = ref('预警整合')
const qh = ref('七类重点')
const value = ref('人像预警')
const Bqvalue = ref('身份预警')
const butStyle = ref()

View File

@ -0,0 +1,203 @@
<template>
<el-dialog :model-value="modelValue" center width="500px" :destroy-on-close="true" :title="title" @close="close"
:close-on-click-modal="false">
<div class="avatar-upload-container">
<div class="avatar-preview">
<img v-if="avatarUrl" :src="avatarUrl" alt="预览头像" class="preview-image">
<div v-else class="preview-placeholder">
<el-icon class="placeholder-icon">
<User />
</el-icon>
<span>请上传头像</span>
</div>
</div>
<div class="upload-section">
<el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false"
:on-change="handleAvatarChange" :before-upload="beforeAvatarUpload" accept="image/*">
<el-button size="small" type="primary">选择图片</el-button>
</el-upload>
<div class="upload-tips">
<p> 支持 JPGPNG 格式</p>
<p> 图片大小不超过 2MB</p>
<p> 建议尺寸为 200x200 像素</p>
</div>
</div>
</div>
<template #footer>
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="confirmUpload" :loading="uploading" :disabled="!avatarUrl || uploading">
{{ uploading ? '上传中...' : '确认更换' }}
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from 'element-plus';
import { User } from '@element-plus/icons-vue';
import { upImageUploadId } from '@/api/commit';
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
title: {
type: String,
default: "更换头像"
},
heightNumber: {
type: Number,
default: 250
}
});
const emits = defineEmits(["update:modelValue", "avatarUpdated"]);
// 头像相关状态
const avatarUrl = ref('');
const uploading = ref(false);
// 监听对话框显示状态,重置头像预览
watch(() => props.modelValue, (newVal) => {
if (!newVal) {
// 对话框关闭时重置头像预览
avatarUrl.value = '';
}
});
// 当前选择的文件
const selectedFile = ref(null);
// 处理头像选择
const handleAvatarChange = (file) => {
selectedFile.value = file.raw;
// 创建临时预览URL
const reader = new FileReader();
reader.onload = (e) => {
avatarUrl.value = e.target.result;
};
reader.readAsDataURL(file.raw);
};
// 上传前检查
const beforeAvatarUpload = (file) => {
const isJPG = file.type === 'image/jpeg';
const isPNG = file.type === 'image/png';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG && !isPNG) {
ElMessage.error('请上传 JPG 或 PNG 格式的图片!');
return false;
}
if (!isLt2M) {
ElMessage.error('图片大小不能超过 2MB!');
return false;
}
return true;
};
// 确认上传头像
const confirmUpload = async () => {
if (!avatarUrl.value || !selectedFile.value) {
ElMessage.warning('请先选择头像图片');
return;
}
uploading.value = true;
try {
// 创建FormData对象
const formData = new FormData();
formData.append('file', selectedFile.value);
// 调用实际的上传接口
const response = await upImageUploadId(formData);
console.log(response);
emits('avatarUpdated', response);
// 关闭对话框
close();
} catch (error) {
console.error('上传头像失败:', error);
ElMessage.error('上传头像失败,请重试');
} finally {
uploading.value = false;
}
};
// 关闭对话框
const close = () => {
emits("update:modelValue", false);
};
</script>
<style lang="scss" scoped>
.avatar-upload-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.avatar-preview {
width: 160px;
height: 160px;
border-radius: 50%;
overflow: hidden;
border: 2px solid #e8e8e8;
margin-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f7fa;
.preview-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.preview-placeholder {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #909399;
.placeholder-icon {
font-size: 48px;
margin-bottom: 8px;
}
span {
font-size: 14px;
}
}
}
.upload-section {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.avatar-uploader {
margin-bottom: 16px;
}
.upload-tips {
width: 100%;
padding: 12px;
background-color: #f5f7fa;
border-radius: 4px;
font-size: 12px;
color: #909399;
p {
margin: 4px 0;
line-height: 1.5;
}
}
</style>

View File

@ -0,0 +1,488 @@
<template>
<div class="comment-list">
<div v-for="comment in comments" :key="comment.id" class="comment-item">
<!-- 一级评论 -->
<div class="comment-main">
<el-avatar :size="40" :src="comment.userAvatar" class="comment-avatar">
<img src="@/assets/images/mr.png" />
</el-avatar>
<div class="comment-content">
<div class="comment-header">
<div class="user-info">
<span class="user-name">{{ comment.userName }}</span>
<div v-if="comment.ssbm" class="author-tag">
{{ comment.ssbm }}
</div>
</div>
</div>
<div class="comment-text">{{ comment.content }}</div>
<div class="comment-footer">
<span class="comment-time">{{ comment.publishTime }}</span>
<div class="comment-actions">
<!-- <div class="action-btn" :class="{ active: comment.isLiked }" @click="handleLike(comment)">
<el-icon>
<Promotion />
</el-icon>
<span>{{ comment.likeCount || 0 }}</span>
</div> -->
<div class="action-btn" @click="handleReply(comment)">
回复
</div>
<div v-if="comment.replies && comment.replies.length > 0" class="action-btn toggle-btn"
@click="toggleReplies(comment)">
{{ comment.showReplies ? '收起' : `展开${comment.replies.length}条回复` }}
<el-icon :class="{ 'rotate-icon': comment.showReplies }">
<ArrowDown />
</el-icon>
</div>
</div>
</div>
<!-- 回复输入框 -->
<transition name="slide-fade">
<div v-if="activeReplyId === comment.id" class="reply-input-box">
<div class="reply-label">回复 {{ comment.userName }}</div>
<el-input v-model="replyContent" type="textarea" :rows="3" placeholder="输入回复内容..."
class="reply-textarea" />
<div class="reply-actions">
<V3Emoji :options-name="optionsName" @click-emoji="onEmojiClick" :recent="true" style="width: 40px;" />
<div class="reply-buttons">
<el-button size="small" @click="cancelReply">取消</el-button>
<el-button size="small" type="primary" @click="submitReply(comment)">
回复
</el-button>
</div>
</div>
</div>
</transition>
<!-- 二级评论列表 -->
<div v-if="comment.replies && comment.replies.length > 0 && comment.showReplies" class="replies-list">
<div v-for="reply in comment.replies" :key="reply.id" class="reply-item">
<el-avatar :size="32" :src="reply.userAvatar" class="reply-avatar">
<img src="@/assets/images/mr.png" />
</el-avatar>
<div class="reply-content">
<div class="reply-header">
<span class="user-name">{{ reply.userName }}</span>
<div v-if="reply.ssbm" class="author-tag">
{{ reply.ssbm }}
</div>
</div>
<div class="reply-text">
<span v-if="reply.replyToUser" class="reply-to">
回复 {{ reply.replyToUser }}:
</span>
{{ reply.content }}
</div>
<div class="reply-footer">
<span class="reply-time">{{ reply.publishTime }}</span>
<div class="reply-actions">
<!-- <div class="action-btn" :class="{ active: reply.isLiked }" @click="handleLike(reply)">
<el-icon>
<Promotion />
</el-icon>
<span v-if="reply.likeCount">{{ reply.likeCount }}</span>
</div> -->
<div class="action-btn" @click="handleReplyToReply(reply, comment)">
回复
</div>
</div>
</div>
<!-- 二级回复输入框 -->
<transition name="slide-fade">
<div v-if="activeReplyId === reply.id" class="reply-input-box">
<div class="reply-label">回复 {{ reply.userName }}</div>
<el-input v-model="replyContent" type="textarea" :rows="3" placeholder="输入回复内容..."
class="reply-textarea" />
<div class="reply-actions">
<V3Emoji :options-name="optionsName" @click-emoji="onEmojiClick" :recent="true"
style="width: 40px;" />
<div class="reply-buttons">
<el-button size="small" @click="cancelReply">取消</el-button>
<el-button size="small" type="primary" @click="submitReply(comment, reply)">
回复
</el-button>
</div>
</div>
</div>
</transition>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Promotion, ArrowDown } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import V3Emoji from 'vue3-emoji'
import { tbGsxtXxltHfid, tbGsxtXxltHfSave, tbGsxtXxltHfSelectList } from '@/api/tbGsxtXxltHf.js'
import { getItem } from '@/utils/storage.js'
import { setAddress } from '@/utils/tools'
const optionsName = {
'Smileys & Emotion': '笑脸&表情',
'Food & Drink': '食物&饮料',
'Animals & Nature': '动物&自然',
'Travel & Places': '旅行&地点',
'People & Body': '人物&身体',
Objects: '物品',
Symbols: '符号',
Flags: '旗帜',
Activities: '活动'
}
const props = defineProps({
comments: {
type: Array,
default: () => []
}, replyTo: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['reply', 'like'])
const activeReplyId = ref(null)
const replyContent = ref('')
const getTagType = (tag) => {
const tagMap = {
'户外活动部': 'success',
'校长': 'warning',
'校本部': 'info'
}
return tagMap[tag] || 'info'
}
const handleLike = (comment) => {
emit('like', comment)
}
const handleReply = (comment) => {
emit('reply')
activeReplyId.value = comment.id
replyContent.value = ''
}
const handleReplyToReply = (reply, parentComment) => {
emit('reply')
activeReplyId.value = reply.id
replyContent.value = `@${reply.userName}:`
}
const onEmojiClick = (emoji) => {
replyContent.value += emoji
}
const cancelReply = () => {
activeReplyId.value = null
replyContent.value = ''
}
// 切换回复列表的展开/收起
const toggleReplies = (comment) => {
if (!comment.showReplies) {
comment.showReplies = true
} else {
comment.showReplies = false
}
}
const formatReplyItem = (reply) => {
return {
...reply,
id: reply.id,
userName: reply.hfrxm || '匿名用户',
userAvatar: reply.userAvatar || (reply.hfrtx ? setAddress(reply.hfrtx) : ''),
userTag: reply.userTag || '',
content: reply.content || reply.hfnr || '',
publishTime: reply.publishTime || reply.hfsj || '',
likeCount: reply.likeCount || 0,
isLiked: reply.isLiked || false,
ssbm: reply.ssbm || '',
replyToUser: reply.replyToUser || reply.sjhfrxm || ''
}
}
const submitReply = async (parentComment, replyToComment = null) => {
console.log(parentComment);
if (!replyContent.value.trim()) {
ElMessage.warning('请输入回复内容')
return
}
try {
const ltmasg = getItem("ltmasg")
let pureContent = replyContent.value
if (pureContent.startsWith('@') && pureContent.includes(':')) {
const colonIndex = pureContent.indexOf(':')
if (colonIndex !== -1 && colonIndex < pureContent.length - 1) {
pureContent = pureContent.substring(colonIndex + 1).trim()
}
}
if (!pureContent) {
ElMessage.warning('请输入回复内容')
return
}
const targetReply = replyToComment || null
const newReply = {
hfnr: pureContent,
hfrsfzh: ltmasg.sfzh,
hfrtx: ltmasg.tx,
hfrxm: ltmasg.xm,
ltid: props.replyTo.id,
sfyjhf: 0,
sjhfid: parentComment.id,
sjhfrxm: targetReply ? targetReply.userName : ''
}
try {
const res = await tbGsxtXxltHfSave(newReply)
if (res) {
const dataxhf = await tbGsxtXxltHfSelectList({ sjhfid: parentComment.id })
const replyList = Array.isArray(dataxhf) ? dataxhf : (dataxhf?.records || [])
parentComment.replies = replyList.map(formatReplyItem)
}
} catch (error) {
console.log(error);
}
// if (!parentComment.replies) {
// parentComment.replies = []
// }
// parentComment.replies.push(newReply)
// 自动展开回复列表
parentComment.showReplies = true
ElMessage.success('回复成功')
cancelReply()
} catch (error) {
console.log(error);
ElMessage.error('回复失败')
}
}
</script>
<style scoped lang="scss">
.comment-list {
.comment-item {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
}
.comment-main {
display: flex;
gap: 12px;
align-items: flex-start;
}
.comment-avatar {
flex-shrink: 0;
}
.comment-content {
flex: 1;
min-width: 0;
padding-top: 2px;
}
.comment-header {
margin-bottom: 8px;
}
.user-info {
display: flex;
align-items: center;
gap: 8px;
}
.user-name {
font-size: 14px;
font-weight: 500;
color: #303133;
}
.comment-text {
font-size: 14px;
line-height: 1.6;
color: #606266;
margin-bottom: 8px;
word-break: break-word;
}
.comment-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.comment-time {
font-size: 12px;
color: #909399;
}
.comment-actions {
display: flex;
gap: 16px;
}
.action-btn {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #909399;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: #409eff;
}
&.active {
color: #409eff;
}
&.toggle-btn {
.el-icon {
transition: transform 0.3s ease;
}
.rotate-icon {
transform: rotate(180deg);
}
}
}
.reply-input-box {
margin-top: 12px;
padding: 12px;
background: #f5f7fa;
border-radius: 8px;
}
.reply-label {
font-size: 12px;
color: #606266;
margin-bottom: 8px;
}
.reply-textarea {
margin-bottom: 8px;
}
.reply-actions {
display: flex;
align-items: center;
justify-content: space-between;
}
.reply-buttons {
display: flex;
gap: 8px;
}
.replies-list {
margin-top: 16px;
padding-left: 12px;
border-left: 2px solid #f0f0f0;
}
.reply-item {
display: flex;
gap: 10px;
margin-bottom: 16px;
align-items: flex-start;
&:last-child {
margin-bottom: 0;
}
}
.reply-avatar {
flex-shrink: 0;
}
.reply-content {
flex: 1;
min-width: 0;
padding-top: 2px;
}
.reply-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 6px;
}
.reply-text {
font-size: 13px;
line-height: 1.6;
color: #606266;
margin-bottom: 6px;
word-break: break-word;
}
.reply-to {
color: #409eff;
margin-right: 4px;
}
.reply-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.reply-time {
font-size: 12px;
color: #909399;
}
.reply-actions {
display: flex;
gap: 12px;
}
.slide-fade-enter-active {
transition: all 0.3s ease;
}
.slide-fade-leave-active {
transition: all 0.2s ease;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateY(-10px);
opacity: 0;
}
.author-tag {
padding: 2px 8px;
border-radius: 4px;
background-color: #409EFF;
color: #fff;
font-size: 12px;
line-height: 18px;
}
</style>

View File

@ -0,0 +1,471 @@
<template>
<div class="post-detail">
<!-- 头部 -->
<div class="detail-header">
<el-button text @click="handleBack">
<el-icon>
<ArrowLeft />
</el-icon>
返回
</el-button>
<div class="header-title">帖子详情</div>
</div>
<!-- 帖子内容 -->
<div class="post-main">
<div class="premium-badge" v-if="postData.isPremium">精品</div>
<div class="post-author">
<el-avatar :size="50" :src="postData.userAvatar">
<img src="@/assets/images/mr.png" />
</el-avatar>
<div class="author-info">
<div class="author-name-row">
<span class="author-name">{{ postData.userName }}</span>
<div v-if="postData.ssbm" class="author-tag">
{{ postData.ssbm }}
</div>
</div>
<div class="publish-time">{{ postData.publishTime }}</div>
</div>
</div>
<div class="post-content-text">{{ postData.content }}</div>
<!-- 图片展示 -->
<div class="post-images" v-if="postData.images && postData.images.length > 0">
<div v-for="(img, index) in postData.images" :key="index" class="image-item">
<el-image :src="img" fit="cover" :preview-src-list="postData.images" :initial-index="index">
<template #error>
<div class="image-error">
<el-icon>
<Picture />
</el-icon>
</div>
</template>
</el-image>
</div>
</div>
<!-- 统计信息 -->
<div class="post-stats">
<div class="stat-item">
<el-icon>
<ChatDotRound />
</el-icon>
<span>{{ postData.commentCount || 0 }}</span>
</div>
<!-- <div class="stat-item" :class="{ active: postData.isLiked }" @click="handleLike">
<el-icon>
<Promotion />
</el-icon>
<span>{{ postData.likeCount || 0 }}</span>
</div> -->
</div>
</div>
<!-- 评论区 -->
<div class="comment-section">
<!-- Tab切换 -->
<div class="comment-tabs">
<div class="tab-item" :class="{ active: activeTab === 'all' }" @click="activeTab = 'all'">
全部回复({{ comments.length }})
</div>
<!-- <div class="tab-item" :class="{ active: activeTab === 'author' }" @click="activeTab = 'author'">
只看楼主
</div> -->
<!-- <div class="sort-buttons">
<el-button text size="small">热门</el-button>
<el-button text size="small">正序</el-button>
<el-button text size="small">倒序</el-button>
</div> -->
</div>
<!-- 顶部输入框 - 点击打开弹窗 -->
<div class="top-input" @click="replyToData">
<el-input placeholder="发点干货 文明第一步" readonly />
</div>
<!-- 评论列表 -->
<CommentList :comments="filteredComments" @reply="handleReply" :replyTo="replyTo" @like="handleCommentLike" />
</div>
<!-- 回复弹窗 -->
<ReplyDialog v-model="showReplyDialog" :reply-to="replyTo" @success="handleReplySuccess" />
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ArrowLeft, ChatDotRound, Promotion, Picture } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import CommentList from './CommentList.vue'
import ReplyDialog from './ReplyDialog.vue'
import { tbGsxtXxltHfid } from '@/api/tbGsxtXxltHf'
import { setAddress } from '@/utils/tools'
const props = defineProps({
postId: {
type: [String, Number],
required: true
}
})
const emit = defineEmits(['back'])
const activeTab = ref('all')
const showReplyDialog = ref(false)
const replyTo = ref(null)
const loading = ref(false)
// 帖子数据
const postData = ref({
id: null,
userName: '',
userAvatar: '',
userTag: '',
publishTime: '',
content: '',
images: [],
commentCount: 0,
likeCount: 0,
isPremium: false,
isLiked: false
})
// 评论数据
const comments = ref([])
onMounted(() => {
loadPostDetail()
})
const loadPostDetail = async () => {
loading.value = true
try {
const res = await tbGsxtXxltHfid(props.postId)
// 设置帖子数据
postData.value = {
id: res.id,
userName: res.fbrxm || '匿名用户',
userAvatar: res.fbrtx ? setAddress(res.fbrtx) : '',
userTag: res.userTag || '',
publishTime: res.time || '',
content: res.content || '',
images: res.tp ? res.tp.split(',').map(img => setAddress(img)) : [],
commentCount: res.commentCount || 0,
likeCount: res.likeCount || 0,
isPremium: res.sfzd === 1,
isLiked: false,
ssbm: res.ssbm,
}
// 设置评论数据
if (res.replyList && res.replyList.length > 0) {
comments.value = res.replyList.map(item => ({
id: item.id,
userName: item.hfrxm || '匿名用户',
userAvatar: item.hfrtx ? setAddress(item.hfrtx) : '',
userTag: item.userTag || '',
content: item.hfnr || '',
publishTime: item.hfsj || '',
likeCount: item.likeCount || 0,
isLiked: false,
showReplies: false,
ssbm: item.ssbm,
replies: item.xjfhList ? item.xjfhList.map(reply => ({
id: reply.id,
userName: reply.hfrxm || '匿名用户',
userAvatar: reply.hfrtx ? setAddress(reply.hfrtx) : '',
userTag: reply.userTag || '',
content: reply.hfnr || '',
publishTime: reply.hfsj || '',
likeCount: reply.likeCount || 0,
isLiked: false,
replyToUser: reply.sjhfrxm || '',
ssbm: reply.ssbm,
})) : []
}))
}
} catch (error) {
console.error('加载详情失败', error)
ElMessage.error('加载详情失败')
} finally {
loading.value = false
}
}
const filteredComments = computed(() => {
if (activeTab.value === 'author') {
return comments.value.filter(c => c.userName === postData.value.userName)
}
console.log(comments.value);
return comments.value
})
const getTagType = (tag) => {
const tagMap = {
'户外活动部': 'success',
'校长': 'warning',
'校本部': 'info'
}
return tagMap[tag] || 'info'
}
const handleBack = () => {
emit('back')
}
const handleLike = () => {
postData.value.isLiked = !postData.value.isLiked
postData.value.likeCount += postData.value.isLiked ? 1 : -1
ElMessage.success(postData.value.isLiked ? '点赞成功' : '取消点赞')
}
const handleReply = () => {
replyTo.value = {
...postData.value,
}
// showReplyDialog.value = true
}
const replyToData = () => {
replyTo.value = {
...postData.value,
}
showReplyDialog.value = true
}
const handleCommentLike = (comment) => {
comment.isLiked = !comment.isLiked
comment.likeCount += comment.isLiked ? 1 : -1
}
const handleReplySuccess = () => {
ElMessage.success('回复成功')
loadPostDetail()
}
</script>
<style scoped lang="scss">
.post-detail {
background: #f5f7fa;
height: 100%;
display: flex;
flex-direction: column;
}
.detail-header {
background: #fff;
padding: 16px 20px;
display: flex;
align-items: center;
border-bottom: 1px solid #f0f0f0;
flex-shrink: 0;
.header-title {
flex: 1;
text-align: center;
font-size: 16px;
font-weight: 500;
color: #303133;
margin-right: 60px;
}
}
.post-main {
background: #fff;
padding: 20px;
margin-bottom: 12px;
position: relative;
flex-shrink: 0;
}
.comment-section {
background: #fff;
padding: 16px 20px 40px 20px;
flex: 1;
overflow-y: auto;
}
.post-main {
background: #fff;
padding: 20px;
margin-bottom: 12px;
position: relative;
flex-shrink: 0;
}
.premium-badge {
position: absolute;
top: 20px;
right: 20px;
background: linear-gradient(135deg, #ff9a56 0%, #ff6b6b 100%);
color: #fff;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.post-author {
display: flex;
gap: 12px;
margin-bottom: 16px;
align-items: flex-start;
}
.author-info {
flex: 1;
padding-top: 2px;
}
.author-name-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.author-name {
font-size: 16px;
font-weight: 500;
color: #303133;
}
.author-tag {
padding: 2px 8px;
border-radius: 4px;
background-color: #409EFF;
color: #fff;
font-size: 12px;
line-height: 18px;
}
.publish-time {
font-size: 12px;
color: #909399;
}
.post-content-text {
font-size: 14px;
line-height: 1.8;
color: #303133;
margin-bottom: 16px;
word-break: break-word;
}
.post-images {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.image-item {
width: 200px;
height: 140px;
border-radius: 8px;
overflow: hidden;
:deep(.el-image) {
width: 100%;
height: 100%;
}
}
.image-error {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #c0c4cc;
font-size: 24px;
}
.post-stats {
display: flex;
gap: 24px;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
color: #909399;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: #409eff;
}
&.active {
color: #409eff;
}
.el-icon {
font-size: 18px;
}
}
.comment-section {
background: #fff;
padding: 16px 20px 40px 20px;
flex: 1;
overflow-y: auto;
}
.comment-tabs {
display: flex;
align-items: center;
margin-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
.tab-item {
padding: 12px 16px;
font-size: 14px;
color: #606266;
cursor: pointer;
position: relative;
transition: all 0.3s ease;
&.active {
color: #409eff;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: #409eff;
}
}
}
.sort-buttons {
margin-left: auto;
display: flex;
gap: 8px;
}
.top-input {
margin-bottom: 20px;
cursor: pointer;
:deep(.el-input__wrapper) {
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,235 @@
<template>
<div class="post-item" @click="handleClick">
<!-- 精品标签 -->
<div class="premium-badge" v-if="post.isPremium">精品</div>
<div class="post-main-content">
<el-avatar :size="50" :src="post.userAvatar" class="post-avatar">
<img src="@/assets/images/mr.png" />
</el-avatar>
<div class="post-right">
<div class="post-header">
<div class="user-name-row">
<span class="user-name">{{ post.userName }}</span>
<div v-if="post.ssbm" class="author-tag">
{{ post.ssbm }}
</div>
</div>
<div class="post-time">{{ post.publishTime }}</div>
</div>
<div class="post-content">
<div class="post-text">{{ post.content }}</div>
<!-- 图片展示 -->
<div class="post-images" v-if="post.images && post.images.length > 0">
<div v-for="(img, index) in post.images" :key="index" class="image-item" @click.stop>
<el-image :src="img" fit="cover" :preview-src-list="post.images" :initial-index="index">
<template #error>
<div class="image-error">
<el-icon>
<Picture />
</el-icon>
</div>
</template>
</el-image>
</div>
</div>
</div>
<div class="post-footer">
<div class="action-item">
<el-icon>
<ChatDotRound />
</el-icon>
<span>{{ post.commentCount || 0 }}</span>
</div>
<!-- <div class="action-item" :class="{ active: post.isLiked }" @click.stop="handleLike">
<el-icon>
<Promotion />
</el-icon>
<span>{{ post.likeCount || 0 }}</span>
</div> -->
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ChatDotRound, Promotion, Picture } from '@element-plus/icons-vue'
const props = defineProps({
post: {
type: Object,
required: true
}
})
const emit = defineEmits(['like', 'click'])
const getTagType = (tag) => {
const tagMap = {
'已动态': 'success',
'校长': 'warning',
'校本部': 'info'
}
return 'warning'
}
const handleLike = () => {
emit('like', props.post)
}
const handleClick = () => {
emit('click', props.post)
}
</script>
<style scoped lang="scss">
.post-item {
background: #fff !important;
border-radius: 8px !important;
padding: 20px !important;
margin-bottom: 16px !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
position: relative !important;
transition: all 0.3s ease !important;
cursor: pointer !important;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12) !important;
}
}
.premium-badge {
position: absolute !important;
top: 16px !important;
right: 16px !important;
background: linear-gradient(135deg, #ff9a56 0%, #ff6b6b 100%) !important;
color: #fff !important;
padding: 4px 12px !important;
border-radius: 4px !important;
font-size: 12px !important;
font-weight: 500 !important;
box-shadow: 0 2px 4px rgba(255, 107, 107, 0.3) !important;
}
.post-main-content {
display: flex !important;
gap: 12px !important;
align-items: flex-start !important;
}
.post-avatar {
flex-shrink: 0 !important;
}
.post-right {
flex: 1 !important;
min-width: 0 !important;
padding-top: 2px !important;
}
.post-header {
margin-bottom: 12px !important;
}
.user-name-row {
display: flex !important;
align-items: center !important;
gap: 8px !important;
margin-bottom: 4px !important;
}
.user-name {
font-size: 16px !important;
font-weight: 500 !important;
color: #303133 !important;
}
.post-time {
font-size: 12px !important;
color: #909399 !important;
}
.post-content {
margin-bottom: 16px !important;
}
.post-text {
font-size: 14px !important;
line-height: 1.6 !important;
color: #606266 !important;
margin-bottom: 12px !important;
word-break: break-word !important;
}
.post-images {
display: flex !important;
gap: 8px !important;
flex-wrap: wrap !important;
}
.image-item {
width: 200px !important;
height: 140px !important;
border-radius: 8px !important;
overflow: hidden !important;
cursor: pointer !important;
:deep(.el-image) {
width: 100% !important;
height: 100% !important;
}
}
.image-error {
display: flex !important;
align-items: center !important;
justify-content: center !important;
width: 100% !important;
height: 100% !important;
background: #f5f7fa !important;
color: #c0c4cc !important;
font-size: 24px !important;
}
.post-footer {
display: flex !important;
gap: 24px !important;
padding-top: 12px !important;
border-top: 1px solid #f0f0f0 !important;
}
.action-item {
display: flex !important;
align-items: center !important;
gap: 6px !important;
color: #909399 !important;
font-size: 14px !important;
cursor: pointer !important;
transition: all 0.3s ease !important;
&:hover {
color: #409eff !important;
}
&.active {
color: #409eff !important;
}
.el-icon {
font-size: 18px !important;
}
}
.author-tag {
padding: 2px 8px;
border-radius: 4px;
background-color: #409EFF;
color: #fff;
font-size: 12px;
line-height: 18px;
}
</style>

View File

@ -0,0 +1,186 @@
<template>
<div class="post-list">
<!-- 发布按钮 -->
<div class="publish-section">
<el-button type="primary" @click="showPublishDialog = true">
<el-icon>
<Edit />
</el-icon>
发布帖子
</el-button>
</div>
<!-- 帖子列表 -->
<div class="posts-container" v-loading="loading" v-infinite-scroll="loadMore"
:infinite-scroll-disabled="scrollDisabled" :infinite-scroll-distance="100">
<PostItem v-for="post in postList" :key="post.id" :post="post" @like="handleLike"
@click="handlePostClick(post)" />
<!-- 加载更多提示 -->
<div v-if="loadingMore" class="loading-more">
<el-icon class="is-loading">
<Loading />
</el-icon>
<span>加载中...</span>
</div>
<!-- 没有更多数据提示 -->
<div v-if="noMore && postList.length > 0" class="no-more">
没有更多数据了
</div>
<!-- 空状态 -->
<el-empty v-if="!loading && postList.length === 0" description="暂无帖子" />
</div>
<!-- 发布对话框 -->
<PublishDialog v-model="showPublishDialog" @success="handlePublishSuccess" />
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import { Edit, Loading } from '@element-plus/icons-vue'
import PostItem from './PostItem.vue'
import PublishDialog from './PublishDialog.vue'
import { ElMessage } from 'element-plus'
import { tbGsxtXxltSelectPage } from '@/api/tbGsxtXxltHf'
import { setAddress } from '@/utils/tools'
const loading = ref(false)
const loadingMore = ref(false)
const postList = ref([])
const showPublishDialog = ref(false)
const listQuery = ref({
pageCurrent: 1,
pageSize: 10
})
const total = ref(0)
// 计算是否禁用滚动加载
const scrollDisabled = computed(() => {
return loadingMore.value || noMore.value
})
// 计算是否没有更多数据
const noMore = computed(() => {
return postList.value.length >= total.value && total.value > 0
})
onMounted(() => {
loadPosts()
})
const loadPosts = async (isLoadMore = false) => {
if (isLoadMore) {
loadingMore.value = true
} else {
loading.value = true
}
try {
const res = await tbGsxtXxltSelectPage(listQuery.value)
const data = (res.records || []).map(item => ({
id: item.id,
userName: item.fbrxm || '匿名用户',
userAvatar: item.fbrtx ? setAddress(item.fbrtx) : '',
userTag: item.userTag || '',
publishTime: item.time || '',
content: item.content || '',
images: item.tp ? item.tp.split(',').map(img => setAddress(img)) : [],
commentCount: item.hfsl || 0,
likeCount: item.likeCount || 0,
isPremium: item.sfzd === 1,
isLiked: false,
ssbm: item.ssbm,
// 保存原始数据
rawData: item
}))
if (isLoadMore) {
postList.value = [...postList.value, ...data]
} else {
postList.value = data
}
total.value = res.total || 0
} catch (error) {
console.error('加载失败', error)
ElMessage.error('加载失败')
} finally {
loading.value = false
loadingMore.value = false
}
}
// 加载更多
const loadMore = () => {
if (postList.value.length >= total.value) {
return
}
listQuery.value.pageCurrent++
loadPosts(true)
}
const handleLike = (post) => {
post.isLiked = !post.isLiked
post.likeCount += post.isLiked ? 1 : -1
ElMessage.success(post.isLiked ? '点赞成功' : '取消点赞')
}
const handlePublishSuccess = () => {
// 重置分页并重新加载
listQuery.value.pageCurrent = 1
loadPosts()
}
const emit = defineEmits(['openDetail'])
const handlePostClick = (post) => {
emit('openDetail', post)
}
</script>
<style scoped lang="scss">
.post-list {
background: transparent;
}
.publish-section {
margin-bottom: 20px;
display: flex;
justify-content: flex-end;
background: #fff;
padding: 16px 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.posts-container {
min-height: 400px;
}
.loading-more {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: #909399;
font-size: 14px;
gap: 8px;
.el-icon {
font-size: 16px;
}
}
.no-more {
text-align: center;
padding: 20px;
color: #c0c4cc;
font-size: 14px;
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<el-dialog v-model="dialogVisible" title="发布帖子" width="600px" :before-close="handleClose">
<el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入帖子标题" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input v-model="form.content" type="textarea" :rows="6" placeholder="请输入帖子内容" maxlength="500"
show-word-limit />
</el-form-item>
<el-form-item label="表情">
<V3Emoji
:options-name="optionsName"
@click-emoji="onEmojiClick"
:recent="true"
/>
</el-form-item>
<el-form-item label="图片">
<Upload v-model="imageIds" :limit="9" :isImg="true" :isAll="true" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
发布
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import V3Emoji from 'vue3-emoji'
import { tbGsxtXxltSave } from '@/api/tbGsxtXxltHf'
import { getItem } from '@/utils/storage.js'
import Upload from '@/components/MyComponents/Upload/index.vue'
const optionsName = {
'Smileys & Emotion': '笑脸&表情',
'Food & Drink': '食物&饮料',
'Animals & Nature': '动物&自然',
'Travel & Places': '旅行&地点',
'People & Body': '人物&身体',
Objects: '物品',
Symbols: '符号',
Flags: '旗帜',
Activities: '活动'
}
const props = defineProps({
modelValue: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:modelValue', 'success'])
const dialogVisible = ref(false)
const formRef = ref()
const submitting = ref(false)
const imageIds = ref([])
const form = ref({
title: '',
content: ''
})
const rules = {
title: [
{ required: true, message: '请输入标题', trigger: 'blur' }
],
content: [
{ required: true, message: '请输入内容', trigger: 'blur' }
]
}
watch(() => props.modelValue, (val) => {
dialogVisible.value = val
})
watch(dialogVisible, (val) => {
emit('update:modelValue', val)
if (!val) {
resetForm()
}
})
const onEmojiClick = (emoji) => {
form.value.content += emoji
}
const handleClose = () => {
dialogVisible.value = false
}
const handleSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (valid) {
submitting.value = true
try {
const ltmasg = getItem('ltmasg')
const postData = {
title: form.value.title,
content: form.value.content,
tp: imageIds.value.join(','),
fbrsfzh: ltmasg?.sfzh || '',
fbrxm: ltmasg?.xm || '',
fbrtx: ltmasg?.tx || ''
}
await tbGsxtXxltSave(postData)
ElMessage.success('发布成功')
emit('success')
handleClose()
} catch (error) {
console.error('发布失败', error)
ElMessage.error('发布失败')
} finally {
submitting.value = false
}
}
})
}
const resetForm = () => {
form.value = {
title: '',
content: ''
}
imageIds.value = []
formRef.value?.resetFields()
}
</script>
<style scoped lang="scss">
// 样式已由Upload组件内部处理</style>

View File

@ -0,0 +1,157 @@
<template>
<el-dialog
v-model="dialogVisible"
title="发表回复"
width="600px"
:before-close="handleClose"
>
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item prop="content">
<el-input
v-model="form.content"
type="textarea"
:rows="8"
placeholder="发点干货 文明第一步"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item>
<div class="emoji-row">
<V3Emoji
:options-name="optionsName"
@click-emoji="onEmojiClick"
:recent="true"
/>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
回复
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import V3Emoji from "vue3-emoji";
import { getItem } from "@/utils/storage.js";
import {
tbGsxtXxltHfid,
tbGsxtXxltHfSave,
tbGsxtXxltHfSelectList
} from "@/api/tbGsxtXxltHf.js";
const optionsName = {
"Smileys & Emotion": "笑脸&表情",
"Food & Drink": "食物&饮料",
"Animals & Nature": "动物&自然",
"Travel & Places": "旅行&地点",
"People & Body": "人物&身体",
Objects: "物品",
Symbols: "符号",
Flags: "旗帜",
Activities: "活动"
};
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
replyTo: {
type: Object,
default: null
}
});
const emit = defineEmits(["update:modelValue", "success"]);
const dialogVisible = ref(false);
const formRef = ref();
const submitting = ref(false);
const form = ref({
content: ""
});
const rules = {
content: [{ required: true, message: "请输入回复内容", trigger: "blur" }]
};
watch(
() => props.modelValue,
(val) => {
dialogVisible.value = val;
}
);
watch(dialogVisible, (val) => {
emit("update:modelValue", val);
if (!val) {
resetForm();
}
});
const onEmojiClick = (emoji) => {
form.value.content += emoji;
};
const handleClose = () => {
dialogVisible.value = false;
};
const handleSubmit = async () => {
if (!formRef.value) return;
await formRef.value.validate(async (valid) => {
if (valid) {
submitting.value = true;
try {
const ltmasg = getItem("ltmasg");
const promes = {
hfnr: form.value.content,
hfrsfzh: ltmasg.sfzh,
hfrtx: ltmasg.tx,
hfrxm: ltmasg.xm,
ltid: props.replyTo.id,
sfyjhf: "1"
// hftp: hfrsfzh.value.hftp ? hfrsfzh.value.hftp.join(',') : ''
};
// 这里替换为实际的API调用
await tbGsxtXxltHfSave(promes);
// await new Promise(resolve => setTimeout(resolve, 1000))
ElMessage.success("回复成功");
emit("success", form.value);
handleClose();
} catch (error) {
console.log(error);
ElMessage.error("回复失败");
} finally {
submitting.value = false;
}
}
});
};
const resetForm = () => {
form.value = {
content: ""
};
formRef.value?.resetFields();
};
</script>
<style scoped lang="scss">
.emoji-row {
display: flex;
align-items: center;
gap: 12px;
}
</style>

View File

@ -0,0 +1,295 @@
<template>
<div class="user-card">
<div class="user-avatar">
<div class="avatar-wrapper" @click="showAvatarDialog = true">
<el-avatar :size="80" :src="avatarUrl">
<img src="@/assets/images/mr.png" />
</el-avatar>
<div class="avatar-overlay">
<el-icon class="upload-icon">
<Camera />
</el-icon>
</div>
</div>
</div>
<div class="user-info">
<div class="info-item clickable" @click="showNicknameDialog = true">
<span class="label">昵称:</span>
<span class="value">{{ userInfo.nickname || '-' }}</span>
<el-icon class="edit-icon">
<Edit />
</el-icon>
</div>
<div class="info-item">
<span class="label">账号:</span>
<span class="value">{{ userInfo.account || '-' }}</span>
</div>
<div class="info-item">
<span class="label">姓名:</span>
<span class="value">{{ userInfo.name || '-' }}</span>
</div>
<div class="info-item">
<span class="label">部门:</span>
<span class="value">{{ userInfo.department || '-' }}</span>
</div>
</div>
</div>
<!-- 更换头像对话框 -->
<ChangeAvatar v-model="showAvatarDialog" title="更换头像" @avatarUpdated="handleAvatarUpdated" />
<!-- 编辑昵称对话框 -->
<el-dialog v-model="showNicknameDialog" title="编辑昵称" width="400px" center :close-on-click-modal="false">
<el-form ref="nicknameFormRef" :model="nicknameForm" :rules="nicknameRules" label-width="80px">
<el-form-item label="昵称" prop="nickname">
<el-input v-model="nicknameForm.nickname" placeholder="请输入昵称" maxlength="20" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showNicknameDialog = false">取消</el-button>
<el-button type="primary" @click="handleSaveNickname">保存</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Camera, Edit } from '@element-plus/icons-vue'
import { getItem, setItem, removeItem } from '@/utils/storage.js'
import { setAddress } from '@/utils/tools'
import { tbGsxtXxltTxTxQueryBySfzh, tbGsxtXxltTxTxSave } from '@/api/tbGsxtXxltHf.js'
import ChangeAvatar from './ChangeAvatar.vue'
const showAvatarDialog = ref(false)
const showNicknameDialog = ref(false)
const nicknameFormRef = ref()
const userInfo = ref({
avatar: '',
account: '',
name: '',
department: '',
nickname: ''
})
const nicknameForm = reactive({
nickname: ''
})
const nicknameRules = {
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' },
{ min: 2, max: 20, message: '昵称长度在 2 到 20 个字符', trigger: 'blur' }
]
}
const avatarUrl = computed(() => {
return userInfo.value.avatar ? setAddress(userInfo.value.avatar) : ''
})
// 加载用户信息
const loadUserInfo = async () => {
const sfzh = getItem('idEntityCard')
let ltmasg = getItem('ltmasg')
if (!ltmasg) {
try {
const res = await tbGsxtXxltTxTxQueryBySfzh({ sfzh: sfzh })
console.log(res);
const deptId = getItem('deptId')?.[0]
ltmasg = {
...res,
deptName: deptId?.deptName || ''
}
setItem('ltmasg', ltmasg)
} catch (error) {
console.error('加载用户信息失败:', error)
}
}
if (ltmasg) {
userInfo.value = {
avatar: ltmasg.tx || '',
account: ltmasg.sfzh || '',
name: ltmasg.xm || '',
department: ltmasg.deptName || ltmasg.bm || '',
nickname: ltmasg.nc || ''
}
}
}
// 处理头像更新
const handleAvatarUpdated = async (newAvatar) => {
try {
const ltmasg = getItem('ltmasg')
const updateData = {
...ltmasg,
tx: newAvatar
}
await tbGsxtXxltTxTxSave(updateData)
removeItem('ltmasg')
await loadUserInfo()
ElMessage.success('头像更新成功')
} catch (error) {
console.error('更新头像失败:', error)
ElMessage.error('头像更新失败,请重试')
}
}
// 处理保存昵称
const handleSaveNickname = async () => {
if (!nicknameFormRef.value) return
await nicknameFormRef.value.validate(async (valid) => {
if (valid) {
try {
const ltmasg = getItem('ltmasg')
const updateData = {
...ltmasg,
nc: nicknameForm.nickname
}
await tbGsxtXxltTxTxSave(updateData)
removeItem('ltmasg')
await loadUserInfo()
showNicknameDialog.value = false
ElMessage.success('昵称保存成功')
} catch (error) {
console.error('保存昵称失败:', error)
ElMessage.error('昵称保存失败,请重试')
}
}
})
}
// 监听昵称对话框打开,初始化表单
const openNicknameDialog = () => {
nicknameForm.nickname = userInfo.value.nickname
}
// 监听对话框显示状态
const unwatchNickname = () => {
if (showNicknameDialog.value) {
openNicknameDialog()
}
}
onMounted(() => {
loadUserInfo()
})
// 监听昵称对话框
const stopWatch = () => {
if (showNicknameDialog.value) {
nicknameForm.nickname = userInfo.value.nickname
}
}
// 使用 watch 监听对话框状态
import { watch } from 'vue'
watch(showNicknameDialog, (newVal) => {
if (newVal) {
nicknameForm.nickname = userInfo.value.nickname
}
})
</script>
<style scoped lang="scss">
.user-card {
background: #fff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.user-avatar {
display: flex;
justify-content: center;
margin-bottom: 20px;
.avatar-wrapper {
position: relative;
cursor: pointer;
border-radius: 50%;
overflow: hidden;
&:hover .avatar-overlay {
opacity: 1;
}
.avatar-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 50%;
.upload-icon {
font-size: 24px;
color: white;
}
}
}
}
.user-info {
.info-item {
display: flex;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
position: relative;
&:last-child {
margin-bottom: 0;
}
&.clickable {
cursor: pointer;
padding: 4px 8px;
margin-left: -8px;
margin-right: -8px;
border-radius: 4px;
transition: background-color 0.3s ease;
&:hover {
background-color: #f5f7fa;
.edit-icon {
opacity: 1;
}
}
}
.label {
color: #909399;
min-width: 50px;
}
.value {
color: #303133;
flex: 1;
word-break: break-all;
}
.edit-icon {
margin-left: 8px;
color: #409eff;
font-size: 14px;
opacity: 0;
transition: opacity 0.3s ease;
}
}
}
</style>

View File

@ -0,0 +1,82 @@
<template>
<div class="luntan-container">
<!-- 列表页 -->
<template v-if="!showDetail">
<div class="luntan-left">
<UserCard />
</div>
<div class="luntan-center">
<PostList @openDetail="handleOpenDetail" />
</div>
<div class="luntan-right">
<!-- 右侧留白区域可以后续添加其他内容 -->
</div>
</template>
<!-- 详情页 -->
<template v-else>
<div class="luntan-left"></div>
<div class="luntan-detail">
<PostDetail :post-id="currentPostId" @back="handleBack" />
</div>
<div class="luntan-right"></div>
</template>
</div>
</template>
<script setup>
import { ref } from 'vue'
import UserCard from './components/UserCard.vue'
import PostList from './components/PostList.vue'
import PostDetail from './components/PostDetail.vue'
const showDetail = ref(false)
const currentPostId = ref(null)
const handleOpenDetail = (post) => {
currentPostId.value = post.id
showDetail.value = true
}
const handleBack = () => {
showDetail.value = false
currentPostId.value = null
}
</script>
<style scoped lang="scss">
.luntan-container {
display: flex;
gap: 20px;
padding: 20px;
background-color: #f5f7fa;
min-height: calc(100vh - 60px);
max-height: calc(100vh - 60px);
overflow: hidden;
}
.luntan-left {
width: 240px;
flex-shrink: 0;
}
.luntan-center {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
.luntan-right {
width: 240px;
flex-shrink: 0;
}
.luntan-detail {
height: 83vh;
flex: 1;
background: #fff;
border-radius: 8px;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -113,6 +113,11 @@ const props = defineProps({
default: () => ({})
}
});
const dialogForm = ref(false); //弹窗
const title = ref("回复帖子");
const listQuery = ref({});
const ListData = ref([])
const optionsName = {
'Smileys & Emotion': '笑脸&表情',
'Food & Drink': '食物&饮料',
@ -124,11 +129,6 @@ const optionsName = {
Flags: '旗帜',
Activities: '活动'
};
const dialogForm = ref(false); //弹窗
const title = ref("回复帖子");
const listQuery = ref({});
const ListData = ref([])
// 初始化数据
const init = (row) => {
dialogForm.value = true;

View File

@ -6,8 +6,13 @@
<template #content>
<el-input type="textarea" placeholder="请输入内容" v-model="hfrsfzh.hfnr"></el-input>
<div style="width: 100%;border-bottom: 1px solid #eee;margin-top: 10px">
<V3Emoji :options-name="optionsName" width="40px" title="表情" @click-emoji="onVue3Emoje"
:recent="true" ></V3Emoji>
<V3Emoji
:options-name="optionsName"
width="40px"
title="表情"
@click-emoji="onVue3Emoje"
:recent="true"
></V3Emoji>
</div>
</template>
</FormMessage>
@ -21,24 +26,12 @@
<script setup>
import "@wangeditor/editor/dist/css/style.css";
import V3Emoji from "vue3-emoji";
import FormMessage from "@/components/aboutTable/FormMessage.vue";
import V3Emoji from 'vue3-emoji'
import { getItem } from '@/utils/storage.js'
import { tbGsxtXxltHfSave } from '@/api/tbGsxtXxltHf'
import { ElMessage } from 'element-plus';
import { ref, defineEmits, defineProps, watch, reactive ,computed} from "vue";
const optionsName = {
'Smileys & Emotion': '笑脸&表情',
'Food & Drink': '食物&饮料',
'Animals & Nature': '动物&自然',
'Travel & Places': '旅行&地点',
'People & Body': '人物&身体',
Objects: '物品',
Symbols: '符号',
Flags: '旗帜',
Activities: '活动'
};
const props = defineProps({
modelValue: {
type: Boolean,
@ -80,6 +73,18 @@ const rules = reactive({
]
})
const optionsName = {
'Smileys & Emotion': '笑脸&表情',
'Food & Drink': '食物&饮料',
'Animals & Nature': '动物&自然',
'Travel & Places': '旅行&地点',
'People & Body': '人物&身体',
Objects: '物品',
Symbols: '符号',
Flags: '旗帜',
Activities: '活动'
};
const onVue3Emoje = (val) => {
hfrsfzh.value.hfnr += val
}

View File

@ -110,6 +110,9 @@
<div class="jqxq-value">{{ fieldValue("bkryqksm") }}</div>
</div>
</div>
<div class="flex just-center">
<el-button @click="closeDialog">关闭</el-button>
</div>
</DialogDragger>
</template>
<script setup>

View File

@ -34,6 +34,9 @@
<div class="jqxq-value">{{ fieldValue("czjg") }}</div>
</div>
</div>
<div class="flex just-center">
<el-button @click="closeDialog">关闭</el-button>
</div>
</DialogDragger>
</template>
<script setup>

View File

@ -39,7 +39,6 @@
<el-date-picker style="width: 100%;" v-model="timeRange" type="datetimerange" start-placeholder="开始时间" end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss" @change="handleTimeRangeChange" />
</div>
</div>
</el-popover>
<el-popover v-else-if="value.label === '事件' && value.onChage" placement="top" :width="480" trigger="click"
@ -195,8 +194,6 @@ getZdsj()
const changeBut = (row) => {
if (row.label == '清除') {
emitter.emit('deletePointArea', 'sj_flash')
emitter.emit('deletePointArea', 'jq_flash')
emitter.emit('deletePointArea', 'jq')
emitter.emit('deletePointArea', 'sj')
return
@ -402,16 +399,16 @@ const updetDz = (row) => {
}
const clickJq = (row) => {
emitter.emit('deletePointArea', 'sj_flash')
emitter.emit('deletePointArea', 'jq_flash')
emitter.emit('deletePointArea', 'sj')
emitter.emit('deletePointArea', 'jq')
if (changeState.value) {
// 添加新的闪烁点位
emitter.emit('addPointArea', { flag: 'jq_flash', coords: [{ jd: row.fxdwjd, wd: row.fxdwwd }], flash: true, offset: [-1, 28] })
emitter.emit('addPointArea', { flag: 'jq', coords: [{...row, jd: row.fxdwjd, wd: row.fxdwwd }], flash: true, offset: [-1, 28] })
// 定位到该点
emitter.emit('setMapCenter', { location: [row.fxdwjd, row.fxdwwd], zoomLevel: 15 })
} else {
// 添加新的闪烁点位
emitter.emit('addPointArea', { flag: 'sj_flash', coords: [{ jd: row.jd, wd: row.wd }], flash: true, offset: [-1, 28] })
emitter.emit('addPointArea', { flag: 'sj', coords: [{ ...row,jd: row.jd, wd: row.wd }], flash: true, offset: [-1, 28] })
// 定位到该点
emitter.emit('setMapCenter', { location: [row.jd, row.wd], zoomLevel: 15 })
}

View File

@ -22,6 +22,7 @@ module.exports = {
publicPath: "./",
outputDir: "gsxt",
assetsDir: "static",
transpileDependencies: ["vue-router"],
lintOnSave: false, //process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {