lcw
This commit is contained in:
@ -7,9 +7,17 @@
|
||||
@close="closed"
|
||||
>
|
||||
<div>
|
||||
<div class="flex" style="margin-bottom: 10px;">
|
||||
<el-button :type="bqLb === '01' ? 'success' : 'info'" @click="qihuan('01')">标签大类</el-button>
|
||||
<el-button :type="bqLb === '02' ? 'success' : 'info'" @click="qihuan('02')"> 标签小类 </el-button>
|
||||
<div class="mark-tabs">
|
||||
<span class="label">标签类型:</span>
|
||||
<el-radio-group v-model="bqLx" @change="changeBqLx">
|
||||
<el-radio-button label="02">行为标签</el-radio-button>
|
||||
<el-radio-button label="01">身份标签</el-radio-button>
|
||||
</el-radio-group>
|
||||
<span class="label" style="margin-left: 20px;">类别:</span>
|
||||
<el-radio-group v-model="bqLb" @change="qihuan">
|
||||
<el-radio-button label="01">大类</el-radio-button>
|
||||
<el-radio-button label="02">小类</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-form :model="listQuery" class="mosty-from-wrap" :inline="true">
|
||||
<el-form-item label="标签名称">
|
||||
@ -83,7 +91,7 @@
|
||||
|
||||
<script setup>
|
||||
import { qcckGet } from "@/api/qcckApi.js";
|
||||
import { defineProps, ref, getCurrentInstance, watch } from "vue";
|
||||
import { defineProps, ref, getCurrentInstance, watch, nextTick } from "vue";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { D_GS_BQ_DJ, D_GS_SSYJ } = proxy.$dict("D_GS_BQ_DJ", "D_GS_SSYJ"); //获取字典数据
|
||||
const props = defineProps({
|
||||
@ -107,6 +115,11 @@ const props = defineProps({
|
||||
roleIds: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
// 标签类型:02-行为标签,01-身份标签
|
||||
bqLx: {
|
||||
type: String,
|
||||
default: "02"
|
||||
}
|
||||
});
|
||||
const loading = ref(false);
|
||||
@ -120,6 +133,8 @@ const keyVal = ref();
|
||||
const multipleUserRef = ref(null);
|
||||
const multipleSelectionUser = ref([]);
|
||||
const tableData = ref([]);
|
||||
const bqLx = ref(props.bqLx);
|
||||
const bqLb = ref("01");
|
||||
const emits = defineEmits(["update:modelValue", "choosed"]);
|
||||
const keyid = (row) => {
|
||||
return row.id;
|
||||
@ -148,9 +163,9 @@ const onComfirm = () => {
|
||||
closed();
|
||||
};
|
||||
const qihuan = (val) => {
|
||||
bqLb.value = val
|
||||
getListData()
|
||||
}
|
||||
listQuery.value.pageCurrent = 1;
|
||||
getListData();
|
||||
};
|
||||
/**
|
||||
* pageSize 改变触发
|
||||
*/
|
||||
@ -168,7 +183,7 @@ const handleCurrentChange = (currentPage) => {
|
||||
const getListData = () => {
|
||||
keyVal.value++;
|
||||
loading.value = true;
|
||||
const params = { ...listQuery.value, bqLx: "02",bqLb:bqLb.value };
|
||||
const params = { ...listQuery.value, bqLx: bqLx.value, bqLb: bqLb.value };
|
||||
qcckGet(params, "/mosty-gsxt/tbGsxtBqgl/selectPage")
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
@ -183,23 +198,25 @@ const getListData = () => {
|
||||
|
||||
//列表回显 - 优化版,确保已选择数据正确回显
|
||||
function multipleUser() {
|
||||
if (!multipleUserRef.value || !tableData.value || tableData.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
if (!multipleUserRef.value || !tableData.value || tableData.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 先清除所有选中状态
|
||||
tableData.value.forEach((item) => {
|
||||
multipleUserRef.value.toggleRowSelection(item, false);
|
||||
});
|
||||
|
||||
// 再根据roleIds重新设置选中状态
|
||||
if (props.roleIds && Array.isArray(props.roleIds) && props.roleIds.length > 0) {
|
||||
// 先清除所有选中状态
|
||||
tableData.value.forEach((item) => {
|
||||
if (props.roleIds.some((id) => id == item.id)) {
|
||||
multipleUserRef.value.toggleRowSelection(item, true);
|
||||
}
|
||||
multipleUserRef.value.toggleRowSelection(item, false);
|
||||
});
|
||||
}
|
||||
|
||||
// 再根据roleIds重新设置选中状态
|
||||
if (props.roleIds && Array.isArray(props.roleIds) && props.roleIds.length > 0) {
|
||||
tableData.value.forEach((item) => {
|
||||
if (props.roleIds.some((id) => id == item.id)) {
|
||||
multipleUserRef.value.toggleRowSelection(item, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const handleFilter = () => {
|
||||
@ -218,7 +235,11 @@ const handleSelectionChange = (val) => {
|
||||
multipleSelectionUser.value = val;
|
||||
}
|
||||
};
|
||||
const bqLb=ref('01')
|
||||
const changeBqLx = (val) => {
|
||||
listQuery.value.pageCurrent = 1;
|
||||
multipleUserRef.value?.clearSelection();
|
||||
getListData();
|
||||
};
|
||||
// 监听弹窗打开状态,打开时重新加载数据
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
@ -233,19 +254,40 @@ watch(
|
||||
// 监听roleIds变化,确保数据回显正确
|
||||
watch(
|
||||
() => props.roleIds,
|
||||
(newRoleIds) => {
|
||||
// 使用setTimeout确保在表格数据加载完成后再进行选择
|
||||
setTimeout(() => {
|
||||
() => {
|
||||
nextTick(() => {
|
||||
multipleUser();
|
||||
}, 100);
|
||||
});
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听外部传入的 bqLx 变化
|
||||
watch(
|
||||
() => props.bqLx,
|
||||
(newVal) => {
|
||||
bqLx.value = newVal;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/css/layout.scss";
|
||||
@import "@/assets/css/element-plus.scss";
|
||||
|
||||
.mark-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px 15px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 6px;
|
||||
|
||||
.label {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.tabBoxRadio .el-checkbox__inner {
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<div class="form-item-box" :class="props.showBtn ? 'showBtn-upload' : ''" :style="{ width: width }">
|
||||
<div
|
||||
class="form-item-box"
|
||||
:class="props.showBtn ? 'showBtn-upload' : ''"
|
||||
:style="{ width: width }"
|
||||
>
|
||||
<el-upload
|
||||
v-bind="$attrs"
|
||||
:headers="headers"
|
||||
@ -13,19 +17,33 @@
|
||||
:before-remove="beforeRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handlerSuccess"
|
||||
:before-upload="beforeImgUpload">
|
||||
:before-upload="beforeImgUpload"
|
||||
>
|
||||
<template #default>
|
||||
<el-button v-if="props.showBtn" size="small" type="primary">上传文件</el-button>
|
||||
<el-button v-if="props.showBtn" size="small" type="primary"
|
||||
>上传文件</el-button
|
||||
>
|
||||
<el-icon v-else><Plus /></el-icon>
|
||||
</template>
|
||||
<template #file="{ file }" v-if="!props.showBtn">
|
||||
<div v-if="props.isImg">
|
||||
<img class="el-upload-list__item-thumbnail" :src="file.url || ''" alt="" />
|
||||
<img
|
||||
class="el-upload-list__item-thumbnail"
|
||||
:src="file.url || ''"
|
||||
alt=""
|
||||
/>
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
|
||||
<span
|
||||
class="el-upload-list__item-preview"
|
||||
@click="handlePictureCardPreview(file)"
|
||||
>
|
||||
<el-icon> <zoom-in /></el-icon>
|
||||
</span>
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
|
||||
<span
|
||||
v-if="!disabled"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file, fileList)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</span>
|
||||
</span>
|
||||
@ -36,10 +54,18 @@
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
</div>
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
|
||||
<span
|
||||
v-if="!disabled"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleDownload(file)"
|
||||
>
|
||||
<el-icon><Download /></el-icon>
|
||||
</span>
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file, fileList)">
|
||||
<span
|
||||
v-if="!disabled"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file, fileList)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</span>
|
||||
</span>
|
||||
@ -54,7 +80,15 @@
|
||||
|
||||
<script setup>
|
||||
import { COMPONENT_WIDTH } from "@/constant";
|
||||
import { ref, defineProps, defineEmits, computed, watch, onMounted, onUnmounted } from "vue";
|
||||
import {
|
||||
ref,
|
||||
defineProps,
|
||||
defineEmits,
|
||||
computed,
|
||||
watch,
|
||||
onMounted,
|
||||
onUnmounted
|
||||
} from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useStore } from "vuex";
|
||||
const props = defineProps({
|
||||
@ -98,35 +132,41 @@ const headers = ref({
|
||||
});
|
||||
const fileList = ref([]);
|
||||
|
||||
watch(() => props.modelValue,(val) => {
|
||||
let arr = val ? (Array.isArray(val) ? val :[val]): [];
|
||||
if(arr.length == 0 ) return fileList.value = [];
|
||||
fileList.value = arr.map((el) => {
|
||||
if (Object.prototype.toString.call(el) === "[object Object]") {
|
||||
// 确保file.url始终是字符串URL
|
||||
const fileUrl = props.isAll ? `/mosty-api/mosty-base/minio/image/download/` + el.id : el.url;
|
||||
return {
|
||||
...el,
|
||||
url: String(fileUrl || ''),
|
||||
name: el.name || '',
|
||||
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
url: String(`/mosty-api/mosty-base/minio/image/download/` + el || ''),
|
||||
id: el
|
||||
};
|
||||
}
|
||||
});
|
||||
console.log(fileList.value, "fileList.value");
|
||||
|
||||
},{ immediate: true,deep:true });
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
let arr = val ? (Array.isArray(val) ? val : [val]) : [];
|
||||
if (arr.length == 0) return (fileList.value = []);
|
||||
fileList.value = arr.map((el) => {
|
||||
if (Object.prototype.toString.call(el) === "[object Object]") {
|
||||
// 确保file.url始终是字符串URL
|
||||
const fileUrl = props.isAll
|
||||
? `/mosty-api/mosty-base/minio/image/download/` + el.id
|
||||
: el.url;
|
||||
return {
|
||||
...el,
|
||||
url: String(fileUrl || ""),
|
||||
name: el.name || ""
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
url: String(`/mosty-api/mosty-base/minio/image/download/` + el || ""),
|
||||
id: el
|
||||
};
|
||||
}
|
||||
});
|
||||
console.log(fileList.value, "fileList.value");
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
const actionUrl = computed(() => {
|
||||
if (props.isAll) {
|
||||
return "/mosty-api/mosty-base/minio/image/upload/id";
|
||||
} else {
|
||||
return props.isImg ? "/mosty-api/mosty-base/minio/image/upload/id": "/mosty-api/mosty-base/minio/file/uploadObj";
|
||||
return props.isImg
|
||||
? "/mosty-api/mosty-base/minio/image/upload/id"
|
||||
: "/mosty-api/mosty-base/minio/file/uploadObj";
|
||||
}
|
||||
});
|
||||
|
||||
@ -158,7 +198,17 @@ const getSuffix = (fileName) => {
|
||||
//pdf
|
||||
if (suffix === "pdf") return "PDF";
|
||||
//视频 音频
|
||||
var videolist = ["mp4","m2v","mkv","rmvb","wmv","avi","flv","mov","m4v"];
|
||||
var videolist = [
|
||||
"mp4",
|
||||
"m2v",
|
||||
"mkv",
|
||||
"rmvb",
|
||||
"wmv",
|
||||
"avi",
|
||||
"flv",
|
||||
"mov",
|
||||
"m4v"
|
||||
];
|
||||
if (videolist.includes(suffix)) return "VIDEO";
|
||||
var musiclist = ["mp3", "wav", "wmv"];
|
||||
if (musiclist.includes(suffix)) return "MUSIC";
|
||||
@ -177,19 +227,19 @@ const handlerSuccess = (res, file) => {
|
||||
file.url = `/mosty-api/mosty-base/minio/image/download/` + res.data;
|
||||
file.id = res.data;
|
||||
fileList.value.push(file);
|
||||
let arr = []
|
||||
if(props.isImg){
|
||||
arr = fileList.value.map((el) => el.id)
|
||||
let arr = [];
|
||||
if (props.isImg) {
|
||||
arr = fileList.value.map((el) => el.id);
|
||||
} else {
|
||||
console.log(fileList,"测试");
|
||||
console.log(fileList, "测试");
|
||||
arr = fileList.value.map((el) => {
|
||||
console.log(el,'xunhuan');
|
||||
console.log(el, "xunhuan");
|
||||
return {
|
||||
id: el.id, name: el.name
|
||||
}
|
||||
})
|
||||
console.log(arr,"测试2222");
|
||||
|
||||
id: el.id,
|
||||
name: el.name
|
||||
};
|
||||
});
|
||||
console.log(arr, "测试2222");
|
||||
}
|
||||
emits("update:modelValue", arr);
|
||||
};
|
||||
@ -201,7 +251,7 @@ const handleExceed = (files, fileList) => {
|
||||
const beforeImgUpload = (file) => {
|
||||
if (props.isImg) {
|
||||
let isIMG = false;
|
||||
if (getSuffix(file.name) === "IMG") isIMG = true;
|
||||
if (getSuffix(file.name) === "IMG") isIMG = true;
|
||||
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||
if (!isIMG) ElMessage.error("上传图片只能是jpg/png/jpeg/bmp/gif格式!");
|
||||
if (!isLt5M) ElMessage.error("上传图片大小不能超过 5MB!");
|
||||
@ -212,7 +262,7 @@ const beforeImgUpload = (file) => {
|
||||
};
|
||||
//查询图片
|
||||
const handlePictureCardPreview = (file) => {
|
||||
dialogImageUrl.value = file.url || '';
|
||||
dialogImageUrl.value = file.url || "";
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
function downloadFile(url, filename) {
|
||||
@ -247,7 +297,7 @@ const beforeRemove = (file) => {
|
||||
});
|
||||
props.modelValue.splice(index, 1);
|
||||
emits("update:modelValue", props.modelValue);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (file) => {
|
||||
let index = fileList.value.findIndex(function (item) {
|
||||
@ -257,7 +307,6 @@ const handleRemove = (file) => {
|
||||
props.modelValue.splice(index, 1);
|
||||
emits("update:modelValue", props.modelValue);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { ref, toRefs, isRef } from 'vue';
|
||||
import { getSysDictByCode, fzdict } from '@/api/sysDict' //引入封装数字字典接口
|
||||
import { ref, toRefs, isRef } from "vue";
|
||||
import { getSysDictByCode, fzdict } from "@/api/sysDict"; //引入封装数字字典接口
|
||||
|
||||
import { getLocalDic } from "@/utils/localDic/index.js"
|
||||
import { getLocalDic } from "@/utils/localDic/index.js";
|
||||
/**
|
||||
* 获取字典数据
|
||||
*/
|
||||
let list = []
|
||||
let list = [];
|
||||
/** 是否取本地字典 (需要本地加载就加这里,不需要就删除) */
|
||||
export function isLocalDict(dictCode) {
|
||||
let localDicObj = {
|
||||
@ -15,9 +15,9 @@ export function isLocalDict(dictCode) {
|
||||
D_GS_SSYJ: true, // "岗哨系统四色预警"
|
||||
D_BZ_SF: true, // "是否"
|
||||
BD_BK_CLYJBQ: true, // "车辆预警标签"
|
||||
D_YJXX_CZCSLX: true, //常控处置措施类型
|
||||
}
|
||||
return localDicObj[dictCode]
|
||||
D_YJXX_CZCSLX: true //常控处置措施类型
|
||||
};
|
||||
return localDicObj[dictCode];
|
||||
}
|
||||
export function getDict(...args) {
|
||||
const res = ref({});
|
||||
@ -26,32 +26,32 @@ export function getDict(...args) {
|
||||
res.value[d] = [];
|
||||
// 本地字典拦截,如果本地字典存在,则使用本地字典,否则使用远程字典
|
||||
if (isLocalDict(d) && getLocalDic(d)) {
|
||||
res.value[d] = getLocalDic(d)
|
||||
res.value[d] = getLocalDic(d);
|
||||
} else {
|
||||
|
||||
getSysDictByCode({
|
||||
dictCode: d
|
||||
}).then(result => {
|
||||
result = result || {}
|
||||
result.itemList = Array.isArray(result.itemList) ? result.itemList : []
|
||||
result.itemList.forEach(p => {
|
||||
p.label = p.zdmc
|
||||
p.value = p.dm
|
||||
p.id = p.dm
|
||||
p.elTagType = p.dictType
|
||||
}).then((result) => {
|
||||
result = result || {};
|
||||
result.itemList = Array.isArray(result.itemList)
|
||||
? result.itemList
|
||||
: [];
|
||||
result.itemList.forEach((p) => {
|
||||
p.label = p.zdmc;
|
||||
p.value = p.dm;
|
||||
p.id = p.dm;
|
||||
p.elTagType = p.dictType;
|
||||
if (p?.itemList && p.itemList?.length > 0) {
|
||||
getChildren(p)
|
||||
getChildren(p);
|
||||
}
|
||||
p.children = p.itemList
|
||||
})
|
||||
res.value[d] = result.itemList
|
||||
p.children = p.itemList;
|
||||
});
|
||||
res.value[d] = result.itemList;
|
||||
//
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
return toRefs(res.value);
|
||||
})()
|
||||
})();
|
||||
}
|
||||
export function getFzDict(...args) {
|
||||
const res = ref({});
|
||||
@ -64,7 +64,7 @@ export function getFzDict(...args) {
|
||||
} else {
|
||||
fzdict({
|
||||
dictLabel: d
|
||||
}).then(result => {
|
||||
}).then((result) => {
|
||||
result = result || {};
|
||||
// result.itemList = Array.isArray(result.itemList) ? result.itemList : [];
|
||||
// result.itemList.forEach(p => {
|
||||
@ -79,26 +79,25 @@ export function getFzDict(...args) {
|
||||
// });
|
||||
// console.log(res.value);
|
||||
|
||||
res.value[d] = result
|
||||
res.value[d] = result;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 使用toRefs确保返回的是响应式对象
|
||||
return toRefs(res.value);
|
||||
})()
|
||||
|
||||
})();
|
||||
}
|
||||
export function getChildren(item) {
|
||||
item.label = item.zdmc
|
||||
item.value = item.dm
|
||||
item.id = item.dm
|
||||
item.label = item.zdmc;
|
||||
item.value = item.dm;
|
||||
item.id = item.dm;
|
||||
if (item.itemList && item.itemList.length > 0) {
|
||||
item.itemList.forEach(v => {
|
||||
getChildren(v)
|
||||
})
|
||||
item.itemList.forEach((v) => {
|
||||
getChildren(v);
|
||||
});
|
||||
}
|
||||
item.children = item.itemList
|
||||
item.children = item.itemList;
|
||||
}
|
||||
/**
|
||||
* 设置级联选择器回显
|
||||
@ -106,17 +105,17 @@ export function getChildren(item) {
|
||||
* @param {*} array 级联数据树
|
||||
* @param {*} childDeptList 子集变量
|
||||
*/
|
||||
export function setCascader(id, array, childDeptList = 'childDeptList', fun) {
|
||||
export function setCascader(id, array, childDeptList = "childDeptList", fun) {
|
||||
if (array) {
|
||||
array.forEach(item => {
|
||||
array.forEach((item) => {
|
||||
if (item.childDeptList && item.id != id) {
|
||||
setCascader(id, item.childDeptList, childDeptList, fun)
|
||||
setCascader(id, item.childDeptList, childDeptList, fun);
|
||||
} else if (item.childDeptList && item.id == id) {
|
||||
fun(item)
|
||||
fun(item);
|
||||
} else if (!item.childDeptList && item.id == id) {
|
||||
fun(item)
|
||||
fun(item);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
@ -127,58 +126,64 @@ export function setCascader(id, array, childDeptList = 'childDeptList', fun) {
|
||||
*/
|
||||
export function IdCard(IdCard, type) {
|
||||
let user = {
|
||||
birthday: '',
|
||||
sex: '',
|
||||
age: ''
|
||||
}
|
||||
if (type === 1 || type == 'all') {
|
||||
birthday: "",
|
||||
sex: "",
|
||||
age: ""
|
||||
};
|
||||
if (type === 1 || type == "all") {
|
||||
//获取出生日期
|
||||
let birthday = IdCard.substring(6, 10) + "-" + IdCard.substring(10, 12) + "-" + IdCard.substring(12, 14)
|
||||
if (type == 'all') {
|
||||
user.birthday = birthday
|
||||
let birthday =
|
||||
IdCard.substring(6, 10) +
|
||||
"-" +
|
||||
IdCard.substring(10, 12) +
|
||||
"-" +
|
||||
IdCard.substring(12, 14);
|
||||
if (type == "all") {
|
||||
user.birthday = birthday;
|
||||
} else {
|
||||
return birthday
|
||||
return birthday;
|
||||
}
|
||||
}
|
||||
if (type === 2 || type == 'all') {
|
||||
if (type === 2 || type == "all") {
|
||||
//获取性别
|
||||
if (parseInt(IdCard.substr(16, 1)) % 2 === 1) {
|
||||
if (type == 'all') {
|
||||
user.sex = '男'
|
||||
if (type == "all") {
|
||||
user.sex = "男";
|
||||
} else {
|
||||
return "男女"
|
||||
return "男";
|
||||
}
|
||||
} else {
|
||||
if (type == 'all') {
|
||||
user.sex = '女'
|
||||
if (type == "all") {
|
||||
user.sex = "女";
|
||||
} else {
|
||||
return "女"
|
||||
return "女";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type === 3 || type == 'all') {
|
||||
if (type === 3 || type == "all") {
|
||||
//获取年龄
|
||||
var ageDate = new Date()
|
||||
var month = ageDate.getMonth() + 1
|
||||
var day = ageDate.getDate()
|
||||
var age = ageDate.getFullYear() - IdCard.substring(6, 10) - 1
|
||||
if (IdCard.substring(10, 12) < month || IdCard.substring(10, 12) === month && IdCard.substring(12, 14) <= day) {
|
||||
age++
|
||||
var ageDate = new Date();
|
||||
var month = ageDate.getMonth() + 1;
|
||||
var day = ageDate.getDate();
|
||||
var age = ageDate.getFullYear() - IdCard.substring(6, 10) - 1;
|
||||
if (
|
||||
IdCard.substring(10, 12) < month ||
|
||||
(IdCard.substring(10, 12) === month && IdCard.substring(12, 14) <= day)
|
||||
) {
|
||||
age++;
|
||||
}
|
||||
if (age <= 0) {
|
||||
age = 1
|
||||
age = 1;
|
||||
}
|
||||
if (type == 'all') {
|
||||
user.age = age
|
||||
if (type == "all") {
|
||||
user.age = age;
|
||||
} else {
|
||||
return age
|
||||
return age;
|
||||
}
|
||||
|
||||
}
|
||||
return user
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*翻译字典数据
|
||||
* @export
|
||||
@ -186,14 +191,14 @@ export function IdCard(IdCard, type) {
|
||||
* @param {*} array
|
||||
*/
|
||||
export function getDictValue(dm, array) {
|
||||
let item = array.value.find(item => {
|
||||
let item = array.value.find((item) => {
|
||||
if (item.value) {
|
||||
return item.value == dm;
|
||||
} else if (item.dm) {
|
||||
return item.dm == dm;
|
||||
}
|
||||
})
|
||||
return item ? item.label : ""
|
||||
});
|
||||
return item ? item.label : "";
|
||||
}
|
||||
|
||||
/** 获取多个字典值(一个值也可以) 字典内容 value-label
|
||||
@ -201,15 +206,15 @@ export function getDictValue(dm, array) {
|
||||
* @param {Array} dict 字典内容
|
||||
*/
|
||||
export function getMultiDictVal(values, dict) {
|
||||
if (typeof values === 'string' && values?.length) values = values.split(',')
|
||||
if (!Array.isArray(values)) return ''
|
||||
if (isRef(dict)) dict = dict.value
|
||||
if (!Array.isArray(dict)) return ''
|
||||
if (typeof values === "string" && values?.length) values = values.split(",");
|
||||
if (!Array.isArray(values)) return "";
|
||||
if (isRef(dict)) dict = dict.value;
|
||||
if (!Array.isArray(dict)) return "";
|
||||
|
||||
return values.map(v => {
|
||||
const item = dict.find(item => item.value === v);
|
||||
return item ? item.label : v;
|
||||
}).join(',');
|
||||
return values
|
||||
.map((v) => {
|
||||
const item = dict.find((item) => item.value === v);
|
||||
return item ? item.label : v;
|
||||
})
|
||||
.join(",");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -13,13 +13,24 @@
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div class="margTop" style="padding: 0;">
|
||||
<WarnDataTable :data="pageData.tableData" :columns="pageData.tableColumn" :tableHeight="pageData.tableHeight"
|
||||
:loading="pageData.tableConfiger.loading" @selection-change="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 }}({{
|
||||
item.bqFz || 0 }} 分) </li>
|
||||
<li
|
||||
:style="{ background: Bqys(item.bqYs) }"
|
||||
class="one_text_detail marks mb4"
|
||||
:key="index"
|
||||
v-for="(item, index) in row.bqList"
|
||||
>
|
||||
{{ item.bqMc }}({{ item.bqFz || 0 }} 分)
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<template #ryXb="{ row }">
|
||||
@ -48,35 +59,93 @@
|
||||
</template>
|
||||
|
||||
<template #xtSjzt="{ row }">
|
||||
<div> {{ row.xtSjzt == 0 ? "注销" : row.xtSjzt == 1 ? "正常" : "封存" }}</div>
|
||||
<div>
|
||||
{{ row.xtSjzt == 0 ? "注销" : row.xtSjzt == 1 ? "正常" : "封存" }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 操作 -->
|
||||
<template #controls="{ row }">
|
||||
<!-- <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'"
|
||||
@click="addEdit('edit', row)">编辑</el-link>
|
||||
<el-link size="small" type="primary" @click="addEdit('detail', row)">详情</el-link>
|
||||
<el-link size="small" type="danger" @click="deleteRow(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'"
|
||||
@click="addEdit('edit', row)"
|
||||
>编辑</el-link
|
||||
>
|
||||
<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>
|
||||
</WarnDataTable>
|
||||
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"></Pages>
|
||||
<Pages
|
||||
@changeNo="changeNo"
|
||||
@changeSize="changeSize"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"
|
||||
></Pages>
|
||||
</div>
|
||||
<!-- 详情 -->
|
||||
<AddForm ref="addFormDiloag" @updateDate="getList"
|
||||
:dic="{ 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 }" />
|
||||
<AddForm
|
||||
ref="addFormDiloag"
|
||||
@updateDate="getList"
|
||||
:dic="{
|
||||
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
|
||||
}"
|
||||
/>
|
||||
<!-- 选择用户 -->
|
||||
<ChooseUser v-model="chooseUserVisible" @choosedUsers="handleUserSelected" :roleIds="roleIds" />
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -91,13 +160,52 @@ import Search from "@/components/aboutTable/Search.vue";
|
||||
import AddForm from "./components/addForm.vue";
|
||||
import { qcckGet, qcckPost, qcckDelete } from "@/api/qcckApi.js";
|
||||
import { reactive, ref, onMounted, getCurrentInstance, nextTick } from "vue";
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { getItem } from "@/utils/storage.js";
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
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_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 {
|
||||
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();
|
||||
@ -134,7 +242,7 @@ const searchConfiger = ref([
|
||||
placeholder: "请输入人员级别",
|
||||
showType: "select",
|
||||
options: D_GS_ZDR_RYJB
|
||||
},
|
||||
}
|
||||
]);
|
||||
const queryFrom = ref({});
|
||||
const pageData = reactive({
|
||||
@ -150,30 +258,48 @@ const pageData = reactive({
|
||||
pageSize: 20,
|
||||
pageCurrent: 1
|
||||
},
|
||||
controlsWidth: 250,
|
||||
tableColumn: [
|
||||
{ label: "姓名", prop: "ryXm", width: 100 },
|
||||
{ label: "性别", prop: "ryXb", slotName: "ryXb", width: 80 },
|
||||
{ label: "身份证", prop: "rySfzh", width: 170 },
|
||||
{ label: "民族", prop: "ryMz", slotName: "ryMz", width: 80 },
|
||||
{ label: "户籍派出所", prop: "hjdPcsmc" },
|
||||
{ label: "标签", prop: "bqList", slotName: "bqList", showOverflowTooltip: true },
|
||||
{
|
||||
label: "标签",
|
||||
prop: "bqList",
|
||||
slotName: "bqList",
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{ label: "协管单位", prop: "gxSsbmmc" },
|
||||
{ label: "管控状态", prop: "zdrBkZt", showOverflowTooltip: true, slotName: "zdrBkZt", 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: 250 },
|
||||
{ label: "入库时间", prop: "zdrRkkssj" },
|
||||
{
|
||||
label: "操作",
|
||||
prop: "controls",
|
||||
slotName: "controls",
|
||||
align: "center",
|
||||
width: 350
|
||||
}
|
||||
]
|
||||
});
|
||||
const isShiQzDelet = ref(false)
|
||||
const isShiQzDelet = ref(false);
|
||||
onMounted(() => {
|
||||
tabHeightFn();
|
||||
const isShiQz = getItem('roleList').find(item => item.roleCode == 'JS_777777') != undefined
|
||||
if (isShiQz) isShiQzDelet.value = true
|
||||
const isShiQz =
|
||||
getItem("roleList").find((item) => item.roleCode == "JS_777777") !=
|
||||
undefined;
|
||||
if (isShiQz) isShiQzDelet.value = true;
|
||||
if (route.query.id) {
|
||||
addEdit('x', {
|
||||
addEdit("x", {
|
||||
id: route.query.id
|
||||
})
|
||||
});
|
||||
} else {
|
||||
getList();
|
||||
}
|
||||
@ -200,14 +326,16 @@ const getList = () => {
|
||||
pageData.tableConfiger.loading = true;
|
||||
// 人员类型D_ZDRY_RYLX(01 重点 02 普通〉
|
||||
// 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;
|
||||
pageData.tableConfiger.loading = false;
|
||||
}).catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
});
|
||||
let data = { ...pageData.pageConfiger, ...queryFrom.value, rylx: "01" };
|
||||
qcckGet(data, "/mosty-gsxt/tbGsxtZdry/selectPage")
|
||||
.then((res) => {
|
||||
pageData.tableData = res.records || [];
|
||||
pageData.total = res.total;
|
||||
pageData.tableConfiger.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
//送审
|
||||
@ -217,25 +345,27 @@ const handleSend = (id) => {
|
||||
proxy.$message({ type: "success", message: "送审成功" });
|
||||
getList();
|
||||
});
|
||||
})
|
||||
});
|
||||
};
|
||||
// 移除
|
||||
const handleremove = (id) => {
|
||||
proxy.$confirm("确定要移除此重点人员?", "警告", { type: "warning" }).then(() => {
|
||||
qcckPost({ id, rylx: '02' }, "/mosty-gsxt/tbGsxtZdry/update").then(() => {
|
||||
proxy.$message({ type: "success", message: "移除成功" });
|
||||
getList();
|
||||
proxy
|
||||
.$confirm("确定要移除此重点人员?", "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
qcckPost({ id, rylx: "02" }, "/mosty-gsxt/tbGsxtZdry/update").then(() => {
|
||||
proxy.$message({ type: "success", message: "移除成功" });
|
||||
getList();
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
const handleMoveToFocus = (id) => {
|
||||
proxy.$confirm("确定要移至关注库?", "警告", { type: "warning" }).then(() => {
|
||||
qcckPost({ id, rylx: '03' }, "/mosty-gsxt/tbGsxtZdry/rylxyd").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) : [];
|
||||
@ -243,14 +373,17 @@ const chooseData = (data) => {
|
||||
};
|
||||
// 选择申请数据数据
|
||||
const handleApplication = () => {
|
||||
if (ids.value.length === 0) return ElMessage.error("请先选择需要布控的重点人");
|
||||
qcckPost({ ids: ids.value }, "/mosty-gsxt/tbGsxtZdry/addBksq").then(() => {
|
||||
ElMessage.success("申请成功");
|
||||
visible.value = false;
|
||||
getList();
|
||||
}).catch(() => {
|
||||
ElMessage.error("布控申请失败");
|
||||
});
|
||||
if (ids.value.length === 0)
|
||||
return ElMessage.error("请先选择需要布控的重点人");
|
||||
qcckPost({ ids: ids.value }, "/mosty-gsxt/tbGsxtZdry/addBksq")
|
||||
.then(() => {
|
||||
ElMessage.success("申请成功");
|
||||
visible.value = false;
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("布控申请失败");
|
||||
});
|
||||
};
|
||||
|
||||
const handleUserSelected = (val) => {
|
||||
@ -260,33 +393,43 @@ const handleUserSelected = (val) => {
|
||||
|
||||
// 处理分配
|
||||
const handlefp = () => {
|
||||
if (ids.value.length === 0) return ElMessage.error("请先选择需要布控的重点人");
|
||||
qcckPost({ ids: ids.value, uid: obj.value.fpid }, "/mosty-gsxt/tbGsxtZdry/addGkmj").then(() => {
|
||||
ElMessage.success("分配成功");
|
||||
visible.value = false;
|
||||
visiblefp.value = false;
|
||||
getList();
|
||||
}).catch(() => {
|
||||
ElMessage.error("分配失败");
|
||||
});
|
||||
if (ids.value.length === 0)
|
||||
return ElMessage.error("请先选择需要布控的重点人");
|
||||
qcckPost(
|
||||
{ ids: ids.value, uid: obj.value.fpid },
|
||||
"/mosty-gsxt/tbGsxtZdry/addGkmj"
|
||||
)
|
||||
.then(() => {
|
||||
ElMessage.success("分配成功");
|
||||
visible.value = false;
|
||||
visiblefp.value = false;
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("分配失败");
|
||||
});
|
||||
};
|
||||
|
||||
// 移交管控
|
||||
const handleMove = () => {
|
||||
if (ids.value.length === 0) return ElMessage.error("请先选择需要移交管控的重点群体");
|
||||
if (ids.value.length === 0)
|
||||
return ElMessage.error("请先选择需要移交管控的重点群体");
|
||||
proxy.$confirm("是否确定移交?", "警告", { type: "warning" }).then(() => {
|
||||
qcckPost({ ids: ids.value }, "/mosty-gsxt/tbGsxtZdry/addSfyj").then(() => {
|
||||
ElMessage.success("移交管控成功");
|
||||
getList();
|
||||
}).catch(() => {
|
||||
ElMessage.error("移交管控失败");
|
||||
});
|
||||
qcckPost({ ids: ids.value }, "/mosty-gsxt/tbGsxtZdry/addSfyj")
|
||||
.then(() => {
|
||||
ElMessage.success("移交管控成功");
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("移交管控失败");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 转线索
|
||||
const handleZxs = () => {
|
||||
if (ids.value.length === 0) return ElMessage.error("请先选择需要转线索的重点群体");
|
||||
if (ids.value.length === 0)
|
||||
return ElMessage.error("请先选择需要转线索的重点群体");
|
||||
showzxs.value = true;
|
||||
nextTick(() => {
|
||||
zxsDilof.value.init(choosList.value);
|
||||
@ -303,22 +446,38 @@ const deleteRow = (id) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//新增编辑
|
||||
const addEdit = (type, row) => {
|
||||
show.value = true;
|
||||
nextTick(() => {
|
||||
addFormDiloag.value.init(type, row);
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
pageData.tableHeight = window.innerHeight - searchBox.value.offsetHeight - 220;
|
||||
pageData.tableHeight =
|
||||
window.innerHeight - searchBox.value.offsetHeight - 220;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
|
||||
// 标签颜色
|
||||
const Bqys = (ys) => {
|
||||
switch (ys) {
|
||||
case "01":
|
||||
return "#fd4343";
|
||||
case "02":
|
||||
return "#c26e09";
|
||||
case "03":
|
||||
return "#ffd208ff";
|
||||
case "04":
|
||||
return "#01abee";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -3,35 +3,60 @@
|
||||
<div class="headClass">
|
||||
<h3>人员信息</h3>
|
||||
<!-- @click="gettbGsxtZdqtUpdate" -->
|
||||
<el-button type="primary" v-if="showBut" :disabled="disabled" @click="submit">保存</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
v-if="showBut"
|
||||
:disabled="disabled"
|
||||
@click="submit"
|
||||
>保存</el-button
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<FormMessage :disabled="disabled" v-model="listQuery" :formList="formData" labelWidth="130px" ref="elform"
|
||||
:rules="rules">
|
||||
<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" />
|
||||
<MOSTY.Upload
|
||||
:showBtn="false"
|
||||
:limit="1"
|
||||
v-model="listQuery.ryzp"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #gkMjXm>
|
||||
<div>
|
||||
<el-input v-model="listQuery.gkMjXm" class="group" placeholder="请输入管控民警姓名" readonly
|
||||
@click="chooseMarksVisible = true" />
|
||||
<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
|
||||
<el-input v-model="listQuery.gkMjSfzh" class="group" placeholder="请输入管控民警身份证号" readonly
|
||||
@click="chooseMarksVisible = true" />
|
||||
</div>
|
||||
</template> -->
|
||||
<!-- { label: "管控民警", prop: "gkMjXm", type: "slot" }, -->
|
||||
<!-- { label: "管控民警身份证号", prop: "gkmjsfzh", type: "slot" }, -->
|
||||
<!-- { label: "管控民警身份证号", prop: "gkMjSfzh", type: "slot" }, -->
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <ChooseMarks v-model="chooseMarksVisible" @choosed="choosed" :roleIds="roleIds" /> -->
|
||||
<ChooseUser v-model="chooseMarksVisible" @choosedUsers="choosed" :roleIds="roleIds" />
|
||||
<ChooseUser
|
||||
v-model="chooseMarksVisible"
|
||||
@choosedUsers="choosed"
|
||||
:roleIds="roleIds"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -43,24 +68,52 @@ import ChooseUser from "@/components/ChooseList/ChooseUser/index.vue";
|
||||
import { ref, reactive, onMounted, getCurrentInstance, watch } from "vue";
|
||||
import { tbGsxtZdryUpdate } from "@/api/zdr.js";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { D_BZ_XB, D_BZ_ZZMM, D_BZ_HYZK, D_BZ_MZ, D_BZ_XZQHDM, D_ZDRY_RYLX, D_BZ_RCBKZT, D_GS_ZDR_RYJB, D_GS_ZDR_YJDJ, D_GS_BK_SSJZ, D_GS_ZDR_CZZT, D_BZ_WHCD, D_ZDRY_ZYLB } =
|
||||
proxy.$dict('D_BZ_XB', 'D_BZ_ZZMM', 'D_BZ_HYZK', 'D_BZ_MZ', "D_ZDRY_RYLX", 'D_BZ_XZQHDM', 'D_BZ_RCBKZT', 'D_GS_ZDR_RYJB', 'D_GS_ZDR_YJDJ', 'D_GS_BK_SSJZ', 'D_GS_ZDR_CZZT', 'D_BZ_WHCD', 'D_ZDRY_ZYLB')
|
||||
const {
|
||||
D_BZ_XB,
|
||||
D_BZ_ZZMM,
|
||||
D_BZ_HYZK,
|
||||
D_BZ_MZ,
|
||||
D_BZ_XZQHDM,
|
||||
D_ZDRY_RYLX,
|
||||
D_BZ_RCBKZT,
|
||||
D_GS_ZDR_RYJB,
|
||||
D_GS_ZDR_YJDJ,
|
||||
D_GS_BK_SSJZ,
|
||||
D_GS_ZDR_CZZT,
|
||||
D_BZ_WHCD,
|
||||
D_ZDRY_ZYLB
|
||||
} = proxy.$dict(
|
||||
"D_BZ_XB",
|
||||
"D_BZ_ZZMM",
|
||||
"D_BZ_HYZK",
|
||||
"D_BZ_MZ",
|
||||
"D_ZDRY_RYLX",
|
||||
"D_BZ_XZQHDM",
|
||||
"D_BZ_RCBKZT",
|
||||
"D_GS_ZDR_RYJB",
|
||||
"D_GS_ZDR_YJDJ",
|
||||
"D_GS_BK_SSJZ",
|
||||
"D_GS_ZDR_CZZT",
|
||||
"D_BZ_WHCD",
|
||||
"D_ZDRY_ZYLB"
|
||||
);
|
||||
const props = defineProps({
|
||||
dataList: {
|
||||
type: Object,
|
||||
default: () => { },
|
||||
}, disabled: {
|
||||
default: () => {}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showBut: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
})
|
||||
}
|
||||
});
|
||||
const rules = reactive({
|
||||
ryXm: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
...rule.identityCardRule({ validator: true }, 'rySfzh'), //身份证校验
|
||||
...rule.identityCardRule({ validator: true }, "rySfzh"), //身份证校验
|
||||
...rule.phoneRule({ validator: true }, "gkMjLxfs"), // 是否必填 是否进行校验,
|
||||
rySfzh: [{ required: true, message: "请输入身份证号", trigger: "blur" }],
|
||||
ryXb: [{ required: true, message: "请选择性别", trigger: "change" }],
|
||||
@ -68,11 +121,15 @@ const rules = reactive({
|
||||
ryCsrq: [{ required: true, message: "请选择出生日期", trigger: "change" }],
|
||||
zdrYjdj: [{ 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" }],
|
||||
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" }],
|
||||
// gkMjSfzh: [{ required: true, message: "请选择管控民警身份证号", trigger: "change" }],
|
||||
// rylx: [{ required: true, message: "请选择人员类型", trigger: "change" }]
|
||||
});
|
||||
const listQuery = ref({}); //表单
|
||||
@ -82,11 +139,35 @@ const formData = ref([
|
||||
{ label: "人员照片", prop: "ryzp", type: "slot", width: "100%" },
|
||||
{ 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: "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: "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%" },
|
||||
@ -94,30 +175,85 @@ const formData = ref([
|
||||
{ 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: "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: "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: "xzdPcsdm",
|
||||
depMc: "xzdPcsmc",
|
||||
type: "department",
|
||||
width: "30%"
|
||||
},
|
||||
|
||||
// { label: "民警身份证", prop: "gkmjsfzh", type: "slot" },
|
||||
// { 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: "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: "Mac地址", prop: "macDz", type: "input", width: "30%" },
|
||||
// { label: "标签选择", prop: "tags", type: "slot", width: "100%" },
|
||||
{ label: "管控原因", prop: "zdrLkyy", type: "textarea", width: "100%" },
|
||||
{ label: "管控原因", prop: "zdrLkyy", type: "textarea", width: "100%" }
|
||||
]);
|
||||
const loading = ref(false);
|
||||
const elform = ref();
|
||||
@ -125,14 +261,14 @@ const disabled = ref(false);
|
||||
// phoneList已重构为listQuery.value.ryLxdh
|
||||
// 创建一个工具函数进行深拷贝
|
||||
const deepClone = (obj) => {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
if (obj === null || typeof obj !== "object") {
|
||||
return obj;
|
||||
}
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
if (obj instanceof Array) {
|
||||
return obj.map(item => deepClone(item));
|
||||
return obj.map((item) => deepClone(item));
|
||||
}
|
||||
const clonedObj = {};
|
||||
for (const key in obj) {
|
||||
@ -143,64 +279,79 @@ 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);
|
||||
// 处理照片数据
|
||||
listQuery.value.ryzp = val.ryzp == null || val.ryzp == '' ? [] : [val.ryzp];
|
||||
listQuery.value.zdrSjjz = val.zdrSjjz == null || val.zdrSjjz == '' ? [] : JSON.parse(val.zdrSjjz);
|
||||
// 处理标签ID数据,确保数据回显
|
||||
if (val.tagIds && Array.isArray(val.tagIds) && val.tagIds.length > 0) {
|
||||
roleIds.value = [...val.tagIds];
|
||||
} else if (val.bqIds && Array.isArray(val.bqIds) && val.bqIds.length > 0) {
|
||||
roleIds.value = [...val.bqIds];
|
||||
} else {
|
||||
roleIds.value = [];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}, { deep: true })
|
||||
);
|
||||
|
||||
// 监听props.dataList变化,处理初始化数据
|
||||
watch(
|
||||
() => props.dataList,
|
||||
(val) => {
|
||||
if (val) {
|
||||
console.log(val);
|
||||
|
||||
// 使用深拷贝避免直接引用同一个对象
|
||||
listQuery.value = deepClone(val);
|
||||
// 处理照片数据
|
||||
listQuery.value.ryzp =
|
||||
val.ryzp == null || val.ryzp == "" ? [] : [val.ryzp];
|
||||
listQuery.value.zdrSjjz =
|
||||
val.zdrSjjz == null || val.zdrSjjz == "" ? [] : JSON.parse(val.zdrSjjz);
|
||||
// 处理标签ID数据,确保数据回显
|
||||
if (val.tagIds && Array.isArray(val.tagIds) && val.tagIds.length > 0) {
|
||||
roleIds.value = [...val.tagIds];
|
||||
} else if (
|
||||
val.bqIds &&
|
||||
Array.isArray(val.bqIds) &&
|
||||
val.bqIds.length > 0
|
||||
) {
|
||||
roleIds.value = [...val.bqIds];
|
||||
} else {
|
||||
roleIds.value = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
// 提交
|
||||
const submit = () => {
|
||||
loading.value = true
|
||||
gettbGsxtZdryUpdate()
|
||||
loading.value = true;
|
||||
gettbGsxtZdryUpdate();
|
||||
};
|
||||
//
|
||||
const gettbGsxtZdryUpdate = () => {
|
||||
const promes = {
|
||||
...listQuery.value,
|
||||
ryzp: listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : "",
|
||||
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz),
|
||||
|
||||
}
|
||||
ryzp:
|
||||
listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : "",
|
||||
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz)
|
||||
};
|
||||
elform.value.submit((data) => {
|
||||
tbGsxtZdryUpdate(promes).then((res) => {
|
||||
listQuery.value.ryzp = []
|
||||
proxy.$message({
|
||||
message: '更新成功',
|
||||
type: 'success',
|
||||
tbGsxtZdryUpdate(promes)
|
||||
.then((res) => {
|
||||
listQuery.value.ryzp = [];
|
||||
proxy.$message({
|
||||
message: "更新成功",
|
||||
type: "success"
|
||||
});
|
||||
})
|
||||
}).catch((err) => {
|
||||
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const throwData = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -209,37 +360,40 @@ const throwData = () => {
|
||||
// 过滤掉空的电话号码
|
||||
resolve({
|
||||
...listQuery.value,
|
||||
ryzp: listQuery.value.ryzp && listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : '',
|
||||
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz),
|
||||
ryzp:
|
||||
listQuery.value.ryzp && listQuery.value.ryzp.length > 0
|
||||
? listQuery.value.ryzp.toString()
|
||||
: "",
|
||||
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz)
|
||||
});
|
||||
})
|
||||
});
|
||||
} else {
|
||||
elform.value.submit((data) => {
|
||||
// 如果没有验证方法,直接返回数据
|
||||
|
||||
resolve({
|
||||
...listQuery.value,
|
||||
ryzp: listQuery.value.ryzp && listQuery.value.ryzp.length > 0 ? listQuery.value.ryzp.toString() : '',
|
||||
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz),
|
||||
ryzp:
|
||||
listQuery.value.ryzp && listQuery.value.ryzp.length > 0
|
||||
? listQuery.value.ryzp.toString()
|
||||
: "",
|
||||
zdrSjjz: JSON.stringify(listQuery.value.zdrSjjz)
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const choosed = (val) => {
|
||||
roleIds.value = [val[0].id]
|
||||
listQuery.value.gkMjXm = val[0].userName
|
||||
listQuery.value.gkmjsfzh = val[0].idEntityCard
|
||||
listQuery.value.gkMjLxfs = val[0].mobile
|
||||
roleIds.value = [val[0].id];
|
||||
listQuery.value.gkMjXm = val[0].userName;
|
||||
listQuery.value.gkMjSfzh = val[0].idEntityCard;
|
||||
listQuery.value.gkMjLxfs = val[0].mobile;
|
||||
listQuery.value.gkMjJh = val[0].inDustRialId;
|
||||
console.log(listQuery.value);
|
||||
|
||||
};
|
||||
defineExpose({
|
||||
throwData,
|
||||
throwData
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -283,7 +437,7 @@ defineExpose({
|
||||
}
|
||||
|
||||
.headClass::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -2px;
|
||||
|
||||
@ -2,10 +2,21 @@
|
||||
<div>
|
||||
<div class="headClass" style="">
|
||||
<h3>人员标签</h3>
|
||||
<el-button type="primary" :disabled="disabled" @click="chooseMarksVisible = true">选择</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="disabled"
|
||||
@click="chooseMarksVisible = true"
|
||||
>选择</el-button
|
||||
>
|
||||
</div>
|
||||
<MyTable :tableData="pageData.tableData" :tableColumn="pageData.tableColumn" :tableHeight="pageData.tableHeight"
|
||||
:key="pageData.keyCount" :tableConfiger="pageData.tableConfiger" :controlsWidth="pageData.controlsWidth">
|
||||
<MyTable
|
||||
:tableData="pageData.tableData"
|
||||
:tableColumn="pageData.tableColumn"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:key="pageData.keyCount"
|
||||
:tableConfiger="pageData.tableConfiger"
|
||||
:controlsWidth="pageData.controlsWidth"
|
||||
>
|
||||
<template #bqLx="{ row }">
|
||||
<DictTag :tag="false" :value="row.bqLx" :options="D_GS_BQ_DJ" />
|
||||
</template>
|
||||
@ -18,143 +29,181 @@
|
||||
</template>
|
||||
</MyTable>
|
||||
</div>
|
||||
<ChooseMarks v-model="chooseMarksVisible" @choosed="addMarks" :roleIds="roleIds" />
|
||||
<ChooseMarks
|
||||
v-model="chooseMarksVisible"
|
||||
@choosed="addMarks"
|
||||
:roleIds="roleIds"
|
||||
:bqLx="defaultBqLx"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, toRaw, getCurrentInstance, onMounted, onUnmounted } from "vue";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
watch,
|
||||
toRaw,
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
onUnmounted
|
||||
} from "vue";
|
||||
import MyTable from "@/components/aboutTable/MyTable.vue";
|
||||
import ChooseMarks from "@/components/ChooseList/ChooseMarks/index.vue";
|
||||
import { tbGsxtZdryUpdate } from '@/api/zdr.js'
|
||||
import { tbGsxtZdryUpdate } from "@/api/zdr.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { D_GS_BQ_DJ, D_GS_SSYJ } = proxy.$dict("D_GS_BQ_DJ", "D_GS_SSYJ"); //获取字典数据
|
||||
const chooseMarksVisible = ref(false)
|
||||
const chooseMarksVisible = ref(false);
|
||||
const props = defineProps({
|
||||
dataList: {
|
||||
type: Object,
|
||||
default: () => { },
|
||||
}, disabled: {
|
||||
default: () => {}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showBut: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
})
|
||||
const listData = ref({})
|
||||
watch(() => props.dataList, (val) => {
|
||||
if (val) {
|
||||
listData.value = val
|
||||
pageData.tableData = val.bqList
|
||||
roleIds.value = val.bqList.map(v => v.bqId)
|
||||
console.log(roleIds.value);
|
||||
|
||||
}
|
||||
}, { deep: true })
|
||||
const roleIds = ref([])
|
||||
});
|
||||
|
||||
const listData = ref({});
|
||||
const roleIds = ref([]);
|
||||
|
||||
watch(
|
||||
() => props.dataList,
|
||||
(val) => {
|
||||
if (val) {
|
||||
listData.value = val;
|
||||
pageData.tableData = val.bqList;
|
||||
roleIds.value = val.bqList.map((v) => v.bqId);
|
||||
console.log(roleIds.value);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 表格数据
|
||||
const pageData = reactive({
|
||||
tableData: [],
|
||||
tableColumn: [{
|
||||
prop: 'bqMc',
|
||||
label: '标签名称',
|
||||
showOverflowTooltip: true
|
||||
}, {
|
||||
prop: 'bqDm',
|
||||
label: '标签代码',
|
||||
}, {
|
||||
showSolt: true,
|
||||
prop: 'bqLx',
|
||||
label: '标签类型',
|
||||
}, {
|
||||
showSolt: true,
|
||||
prop: 'bqLb',
|
||||
label: '标签类别',
|
||||
}],
|
||||
tableHeight: '200px',
|
||||
tableColumn: [
|
||||
{
|
||||
prop: "bqMc",
|
||||
label: "标签名称",
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: "bqDm",
|
||||
label: "标签代码"
|
||||
},
|
||||
{
|
||||
showSolt: true,
|
||||
prop: "bqLx",
|
||||
label: "标签类型"
|
||||
},
|
||||
{
|
||||
showSolt: true,
|
||||
prop: "bqLb",
|
||||
label: "标签类别"
|
||||
}
|
||||
],
|
||||
tableHeight: "200px",
|
||||
keyCount: 0,
|
||||
tableConfiger: {
|
||||
border: true,
|
||||
stripe: true,
|
||||
showHeader: true,
|
||||
showIndex: true,
|
||||
indexLabel: '序号',
|
||||
indexLabel: "序号",
|
||||
indexWidth: 60,
|
||||
align: 'center',
|
||||
align: "center",
|
||||
showOverflowTooltip: true,
|
||||
haveControls: !props.disabled
|
||||
},
|
||||
controlsWidth: 200,
|
||||
})
|
||||
controlsWidth: 200
|
||||
});
|
||||
|
||||
// 计算默认的标签类型:如果已选数据中有身份标签则默认显示身份标签,否则默认显示行为标签
|
||||
const defaultBqLx = computed(() => {
|
||||
if (roleIds.value && roleIds.value.length > 0) {
|
||||
const selectedItem = pageData.tableData.find((item) => roleIds.value.includes(item.bqId));
|
||||
if (selectedItem && selectedItem.bqLx) {
|
||||
return selectedItem.bqLx;
|
||||
}
|
||||
}
|
||||
return "02";
|
||||
});
|
||||
// 修改数据接口
|
||||
const zdqtUpdate = (val) => {
|
||||
const params = {
|
||||
id: listData.value.id,
|
||||
bqList: pageData.tableData,
|
||||
rySfzh: listData.value.rySfzh,
|
||||
}
|
||||
tbGsxtZdryUpdate(params).then(res => {
|
||||
rySfzh: listData.value.rySfzh
|
||||
};
|
||||
tbGsxtZdryUpdate(params).then((res) => {
|
||||
proxy.$message({
|
||||
message: val,
|
||||
type: 'success'
|
||||
})
|
||||
})
|
||||
}
|
||||
type: "success"
|
||||
});
|
||||
});
|
||||
};
|
||||
// 新增标签
|
||||
const addMarks = (val) => {
|
||||
pageData.tableData = val.map(v => {
|
||||
return { bqDm: v.bqDm, bqId: v.id, bqLb: v.bqLb, bqLx: v.bqLx, bqMc: v.bqMc }
|
||||
pageData.tableData = val.map((v) => {
|
||||
return {
|
||||
bqDm: v.bqDm,
|
||||
bqId: v.id,
|
||||
bqLb: v.bqLb,
|
||||
bqLx: v.bqLx,
|
||||
bqMc: v.bqMc,
|
||||
bqYs: v.bqYs
|
||||
};
|
||||
});
|
||||
roleIds.value = val.map(v => v.id)
|
||||
roleIds.value = val.map((v) => v.id);
|
||||
if (!props.disabled && props.showBut) {
|
||||
zdqtUpdate("标签添加成功")
|
||||
zdqtUpdate("标签添加成功");
|
||||
}
|
||||
}
|
||||
};
|
||||
// 删除标签
|
||||
const delDictItem = (val) => {
|
||||
if (!props.disabled && props.showBut) {
|
||||
ElMessageBox.confirm(
|
||||
'是否删除标签',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
pageData.tableData = pageData.tableData.filter(v => v.bqId != val)
|
||||
roleIds.value = roleIds.value.filter(v => v != val)
|
||||
zdqtUpdate("标签删除成功")
|
||||
ElMessageBox.confirm("是否删除标签", "提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: '取消删除',
|
||||
.then(() => {
|
||||
pageData.tableData = pageData.tableData.filter((v) => v.bqId != val);
|
||||
roleIds.value = roleIds.value.filter((v) => v != val);
|
||||
zdqtUpdate("标签删除成功");
|
||||
})
|
||||
})
|
||||
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: "info",
|
||||
message: "取消删除"
|
||||
});
|
||||
});
|
||||
} else {
|
||||
pageData.tableData = pageData.tableData.filter(v => v.bqId != val)
|
||||
roleIds.value = roleIds.value.filter(v => v != val)
|
||||
pageData.tableData = pageData.tableData.filter((v) => v.bqId != val);
|
||||
roleIds.value = roleIds.value.filter((v) => v != val);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
// 抛出数据并验证标签列表不为空
|
||||
const throwData = () => {
|
||||
return new Promise((resolve) => {
|
||||
// 验证:确保标签列表不为空
|
||||
if (!pageData.tableData || pageData.tableData.length === 0) {
|
||||
throw new Error('请选择群体标签');
|
||||
throw new Error("请选择群体标签");
|
||||
}
|
||||
resolve(pageData.tableData);
|
||||
});
|
||||
}
|
||||
};
|
||||
defineExpose({
|
||||
throwData
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -196,7 +245,7 @@ defineExpose({
|
||||
}
|
||||
|
||||
.headClass::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -2px;
|
||||
|
||||
@ -4,34 +4,95 @@
|
||||
<div class="query-grid">
|
||||
<div v-for="field in renderFields" :key="field.key" class="query-cell">
|
||||
<div class="cell-label">{{ field.label }}</div>
|
||||
<div class="cell-control" :class="{ 'is-checkbox': field.type === 'checkbox' }">
|
||||
<el-input clearable v-if="field.type === 'input'" v-model="formState[field.key]" class="control-input"
|
||||
:placeholder="field.placeholder || ''" />
|
||||
<el-input clearable v-else-if="field.type === 'number'" v-model="formState[field.key]" class="control-input"
|
||||
type="number" :placeholder="field.placeholder || ''" />
|
||||
<el-select clearable v-else-if="field.type === 'select'" v-model="formState[field.key]" class="control-select"
|
||||
:placeholder="field.placeholder || '请选择'" :multiple="field.multiple || false" collapse-tags
|
||||
collapse-tags-tooltip>
|
||||
<el-option v-for="item in field.options || []" :key="item.value ?? item" :label="item.label ?? item"
|
||||
:value="item.value ?? item" />
|
||||
<div
|
||||
class="cell-control"
|
||||
:class="{ 'is-checkbox': field.type === 'checkbox' }"
|
||||
>
|
||||
<el-input
|
||||
clearable
|
||||
v-if="field.type === 'input'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-input"
|
||||
:placeholder="field.placeholder || ''"
|
||||
/>
|
||||
<el-input
|
||||
clearable
|
||||
v-else-if="field.type === 'number'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-input"
|
||||
type="number"
|
||||
:placeholder="field.placeholder || ''"
|
||||
/>
|
||||
<el-select
|
||||
clearable
|
||||
v-else-if="field.type === 'select'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-select"
|
||||
:placeholder="field.placeholder || '请选择'"
|
||||
:multiple="field.multiple || false"
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
>
|
||||
<el-option
|
||||
v-for="item in field.options || []"
|
||||
:key="item.value ?? item"
|
||||
:label="item.label ?? item"
|
||||
:value="item.value ?? item"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker clearable v-else-if="field.type === 'date'" v-model="formState[field.key]"
|
||||
class="control-date" type="date" :placeholder="field.placeholder || '请选择日期'" value-format="YYYY-MM-DD" />
|
||||
<el-date-picker clearable v-else-if="field.type === 'datetime'" v-model="formState[field.key]"
|
||||
class="control-date" type="datetime" :placeholder="field.placeholder || '请选择时间'"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" />
|
||||
<el-date-picker clearable v-else-if="field.type === 'daterange'" v-model="formState[field.key]"
|
||||
class="control-date" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD" />
|
||||
<el-date-picker clearable v-else-if="field.type === 'datetimerange'" v-model="formState[field.key]"
|
||||
class="control-date" type="datetimerange" range-separator="至" start-placeholder="开始时间"
|
||||
end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" />
|
||||
<el-date-picker
|
||||
clearable
|
||||
v-else-if="field.type === 'date'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-date"
|
||||
type="date"
|
||||
:placeholder="field.placeholder || '请选择日期'"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
<el-date-picker
|
||||
clearable
|
||||
v-else-if="field.type === 'datetime'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-date"
|
||||
type="datetime"
|
||||
:placeholder="field.placeholder || '请选择时间'"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
<el-date-picker
|
||||
clearable
|
||||
v-else-if="field.type === 'daterange'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-date"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
<el-date-picker
|
||||
clearable
|
||||
v-else-if="field.type === 'datetimerange'"
|
||||
v-model="formState[field.key]"
|
||||
class="control-date"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
<template v-else-if="field.type === 'department'">
|
||||
<MOSTY.Department clearable v-model="formState[field.key]" class="control-select" />
|
||||
<MOSTY.Department
|
||||
clearable
|
||||
v-model="formState[field.key]"
|
||||
class="control-select"
|
||||
/>
|
||||
</template>
|
||||
<div v-else-if="field.type === 'checkbox'" class=" checkbox-wrap">
|
||||
<div v-else-if="field.type === 'checkbox'" class="checkbox-wrap">
|
||||
<el-checkbox v-model="formState[field.key]" />
|
||||
</div>
|
||||
<div v-else-if="field.type === 'slot'" class="checkbox-wrap">
|
||||
<slot :name="field.key" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -40,24 +101,28 @@
|
||||
<slot name="but"></slot>
|
||||
</div>
|
||||
<div>
|
||||
<el-button size="small" type="primary" @click="handleSearch">{{ searchText }}</el-button>
|
||||
<el-button size="small" type="button" @click="handleReset">重置 </el-button>
|
||||
<el-button size="small" type="primary" @click="handleSearch">{{
|
||||
searchText
|
||||
}}</el-button>
|
||||
<el-button size="small" type="button" @click="handleReset"
|
||||
>重置
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, watch } from 'vue'
|
||||
import { computed, reactive, watch } from "vue";
|
||||
import * as MOSTY from "@/components/MyComponents/index";
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '查询条件'
|
||||
default: "查询条件"
|
||||
},
|
||||
searchText: {
|
||||
type: String,
|
||||
default: '查询'
|
||||
default: "查询"
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
@ -67,100 +132,106 @@ const props = defineProps({
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['search', 'submit', 'reset'])
|
||||
const formState = reactive({})
|
||||
const emit = defineEmits(["search", "submit", "reset"]);
|
||||
const formState = reactive({});
|
||||
|
||||
const renderFields = computed(() => {
|
||||
const source = props.searchArr.length ? props.searchArr : props.fields
|
||||
return source.map((field) => ({
|
||||
...field,
|
||||
key: field.key || field.prop,
|
||||
type: field.type || field.showType || 'input'
|
||||
})).filter((field) => field.key)
|
||||
})
|
||||
const source = props.searchArr.length ? props.searchArr : props.fields;
|
||||
return source
|
||||
.map((field) => ({
|
||||
...field,
|
||||
key: field.key || field.prop,
|
||||
type: field.type || field.showType || "input"
|
||||
}))
|
||||
.filter((field) => field.key);
|
||||
});
|
||||
|
||||
const getResetValue = (field) => {
|
||||
if (field.defaultVal !== undefined) {
|
||||
return Array.isArray(field.defaultVal) ? [...field.defaultVal] : field.defaultVal
|
||||
return Array.isArray(field.defaultVal)
|
||||
? [...field.defaultVal]
|
||||
: field.defaultVal;
|
||||
}
|
||||
if (field.type === 'checkbox') {
|
||||
return false
|
||||
if (field.type === "checkbox") {
|
||||
return false;
|
||||
}
|
||||
if (field.type === 'daterange' || field.type === 'datetimerange') {
|
||||
return []
|
||||
if (field.type === "daterange" || field.type === "datetimerange") {
|
||||
return [];
|
||||
}
|
||||
if (field.type === 'select') {
|
||||
if (field.type === "select") {
|
||||
if (field.multiple) {
|
||||
return []
|
||||
return [];
|
||||
}
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
return ''
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const buildResetPayload = () => {
|
||||
const payload = {}
|
||||
const payload = {};
|
||||
renderFields.value.forEach((field) => {
|
||||
payload[field.key] = getResetValue(field)
|
||||
})
|
||||
return payload
|
||||
}
|
||||
payload[field.key] = getResetValue(field);
|
||||
});
|
||||
return payload;
|
||||
};
|
||||
|
||||
const setFormState = (value = {}) => {
|
||||
Object.keys(formState).forEach((key) => {
|
||||
delete formState[key]
|
||||
})
|
||||
delete formState[key];
|
||||
});
|
||||
renderFields.value.forEach((field) => {
|
||||
if (value[field.key] !== undefined) {
|
||||
formState[field.key] = Array.isArray(value[field.key]) ? [...value[field.key]] : value[field.key]
|
||||
return
|
||||
formState[field.key] = Array.isArray(value[field.key])
|
||||
? [...value[field.key]]
|
||||
: value[field.key];
|
||||
return;
|
||||
}
|
||||
formState[field.key] = getResetValue(field)
|
||||
})
|
||||
}
|
||||
formState[field.key] = getResetValue(field);
|
||||
});
|
||||
};
|
||||
|
||||
const getFormSnapshot = () => {
|
||||
const snapshot = {}
|
||||
const snapshot = {};
|
||||
renderFields.value.forEach((field) => {
|
||||
const value = formState[field.key]
|
||||
const value = formState[field.key];
|
||||
if (Array.isArray(value)) {
|
||||
snapshot[field.key] = [...value]
|
||||
return
|
||||
snapshot[field.key] = [...value];
|
||||
return;
|
||||
}
|
||||
snapshot[field.key] = value
|
||||
})
|
||||
return snapshot
|
||||
}
|
||||
snapshot[field.key] = value;
|
||||
});
|
||||
return snapshot;
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
const payload = getFormSnapshot()
|
||||
emit('search', payload)
|
||||
emit('submit', payload)
|
||||
}
|
||||
const payload = getFormSnapshot();
|
||||
emit("search", payload);
|
||||
emit("submit", payload);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
const payload = buildResetPayload()
|
||||
setFormState(payload)
|
||||
emit('reset', true)
|
||||
emit('search', payload)
|
||||
emit('submit', payload)
|
||||
}
|
||||
const payload = buildResetPayload();
|
||||
setFormState(payload);
|
||||
emit("reset", true);
|
||||
emit("search", payload);
|
||||
emit("submit", payload);
|
||||
};
|
||||
|
||||
watch(
|
||||
renderFields,
|
||||
() => {
|
||||
setFormState(buildResetPayload())
|
||||
setFormState(buildResetPayload());
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
formState,
|
||||
handleSearch,
|
||||
handleReset
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -1,32 +1,70 @@
|
||||
<template>
|
||||
<div>
|
||||
<PageTitle :malginLeft="10" :height="35" backgroundColor="#ffff" :marginBottom="5" :marginTop="5">
|
||||
<PageTitle
|
||||
:malginLeft="10"
|
||||
:height="35"
|
||||
backgroundColor="#ffff"
|
||||
:marginBottom="5"
|
||||
:marginTop="5"
|
||||
>
|
||||
<template #left>
|
||||
<!-- -->
|
||||
<template v-for="(item, index) in butList" :key="index">
|
||||
<el-popover placement="right" :width="240" style='height: 300px;' trigger="click" v-if="item == '布控预警'">
|
||||
<!-- <el-popover
|
||||
placement="right"
|
||||
:width="240"
|
||||
style="height: 300px"
|
||||
trigger="click"
|
||||
v-if="item == '布控预警'"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button :type="butStylChange(qh) ? 'primary' : 'default'" size="small">{{ item }}</el-button>
|
||||
<el-button
|
||||
:type="butStylChange(qh) ? 'primary' : 'default'"
|
||||
size="small"
|
||||
>{{ item }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-select v-model="value" placeholder="请选择预警类型" @change="qh = value">
|
||||
<el-select
|
||||
v-model="value"
|
||||
placeholder="请选择预警类型"
|
||||
@change="qh = value"
|
||||
>
|
||||
<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> -->
|
||||
<!-- <el-popover
|
||||
placement="right"
|
||||
:width="240"
|
||||
style="height: 300px"
|
||||
trigger="click"
|
||||
v-if="item == '标签预警'"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button :type="BqbutStylChange(qh) ? 'primary' : 'default'" size="small">{{ item }}</el-button>
|
||||
<el-button
|
||||
:type="BqbutStylChange(qh) ? 'primary' : 'default'"
|
||||
size="small"
|
||||
>{{ item }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-select v-model="Bqvalue" placeholder="请选择预警类型" @change="qh = Bqvalue">
|
||||
<el-select
|
||||
v-model="Bqvalue"
|
||||
placeholder="请选择预警类型"
|
||||
@change="qh = Bqvalue"
|
||||
>
|
||||
<el-option label="身份预警" value="身份预警" />
|
||||
<el-option label="行为预警" value="行为预警" />
|
||||
<el-option label="组合预警" value="组合预警" />
|
||||
</el-select>
|
||||
</el-popover>
|
||||
<el-button :type="qh == item ? 'primary' : 'default'" @click="qh = item" size="small" v-else>{{ item
|
||||
}}</el-button>
|
||||
</el-popover> -->
|
||||
<el-button
|
||||
:type="qh == item ? 'primary' : 'default'"
|
||||
@click="qh = item"
|
||||
size="small"
|
||||
>{{ item }}</el-button
|
||||
>
|
||||
</template>
|
||||
</template>
|
||||
</PageTitle>
|
||||
@ -41,57 +79,63 @@
|
||||
<IdentityWarning v-if="qh == '身份预警'" />
|
||||
<BehaviorWarning v-if="qh == '行为预警'" />
|
||||
<CombinedWarning v-if="qh == '组合预警'" />
|
||||
<LabelWarning v-if="qh == '标签预警'" />
|
||||
<!-- <Cs v-if="qh == '测试'" /> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { getItem } from '@//utils/storage.js'
|
||||
import { getItem } from "@//utils/storage.js";
|
||||
import PageTitle from "@/components/aboutTable/PageTitle.vue";
|
||||
import FouColorWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/fouColorWarning/index.vue"
|
||||
import SevenWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/sevenWarning/index.vue"
|
||||
import IdentityWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/identityWarning/index.vue"
|
||||
import BehaviorWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/behaviorWarning/index.vue"
|
||||
import CombinedWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/combinedWarning/index.vue"
|
||||
import PortraitWarning from "@/views/backOfficeSystem/fourColorManage/warningList/portraitWarning/index.vue"
|
||||
import VehicleWarning from "@/views/backOfficeSystem/fourColorManage/warningList/vehicleWarning/index.vue"
|
||||
import ControlWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/controlWarning/index.vue"
|
||||
import RegionalControl from "@/views/backOfficeSystem/fourColorManage/warningControl/regionalControl/index.vue"
|
||||
import WrjWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/wrjWarning/index.vue"
|
||||
import PoliticalSecurityWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/politicalSecurity/index.vue"
|
||||
|
||||
import Cs from '@/views/backOfficeSystem/ces/index.vue'
|
||||
import FouColorWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/fouColorWarning/index.vue";
|
||||
import SevenWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/sevenWarning/index.vue";
|
||||
import IdentityWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/identityWarning/index.vue";
|
||||
import BehaviorWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/behaviorWarning/index.vue";
|
||||
import CombinedWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/combinedWarning/index.vue";
|
||||
import PortraitWarning from "@/views/backOfficeSystem/fourColorManage/warningList/portraitWarning/index.vue";
|
||||
import VehicleWarning from "@/views/backOfficeSystem/fourColorManage/warningList/vehicleWarning/index.vue";
|
||||
// import ControlWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/controlWarning/index.vue";
|
||||
import RegionalControl from "@/views/backOfficeSystem/fourColorManage/warningControl/regionalControl/index.vue";
|
||||
import WrjWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/wrjWarning/index.vue";
|
||||
import PoliticalSecurityWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/politicalSecurity/index.vue";
|
||||
import ControlWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/deploymentIntegration/index.vue";
|
||||
import LabelWarning from "@/views/backOfficeSystem/fourColorManage/warningControl/iabelWarning/index.vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
// "人像预警", "车辆预警",, "区域预警","无人机预警"
|
||||
const butList = ref(["七类重点", '政保预警', "布控预警", "预警整合"])
|
||||
const qh = ref('七类重点')
|
||||
const value = ref('人像预警')
|
||||
const Bqvalue = ref('身份预警')
|
||||
const butStyle = ref()
|
||||
const hasPermissin = ref(false)
|
||||
const butList = ref(["七类重点", "政保预警", "布控预警", "预警整合"]);
|
||||
const qh = ref("七类重点");
|
||||
const value = ref("人像预警");
|
||||
const Bqvalue = ref("身份预警");
|
||||
const butStyle = ref();
|
||||
const hasPermissin = ref(false);
|
||||
const butStylChange = (val) => {
|
||||
return ["人像预警", "车辆预警", "区域预警", "布控预警"].includes(val)
|
||||
|
||||
}
|
||||
return ["人像预警", "车辆预警", "区域预警", "布控预警"].includes(val);
|
||||
};
|
||||
const BqbutStylChange = (val) => {
|
||||
return ["身份预警", "行为预警", "组合预警"].includes(val)
|
||||
}
|
||||
return ["身份预警", "行为预警", "组合预警"].includes(val);
|
||||
};
|
||||
onMounted(() => {
|
||||
// rolCode : 市情指领导(JS_666666)、市情指权限(JS_777777)、县情指权限(JS_888888)、县情指领导权限(JS_999999);
|
||||
// depCode : 市情指领导(513030199509084123 )、市情指(340827200404141028)、县情指领导(540421196805217650)、朗县公安局指挥中心(县情指)(540422200010197030)、朗县公安局县城派出所(部门)(513425199305205211)
|
||||
let rolCode = ['JS_666666', 'JS_777777', 'JS_888888', 'JS_999999']
|
||||
let depCode = ['513030199509084123', '340827200404141028', '540421196805217650', '540422200010197030', '513425199305205211']
|
||||
let roleData = getItem('roleList');
|
||||
let deptData = getItem('deptId');
|
||||
roleData.forEach(item => {
|
||||
if (rolCode.includes(item.roleCode)) hasPermissin.value = true
|
||||
})
|
||||
deptData.forEach(item => {
|
||||
if (depCode.includes(item.deptCode)) hasPermissin.value = true
|
||||
})
|
||||
let rolCode = ["JS_666666", "JS_777777", "JS_888888", "JS_999999"];
|
||||
// let depCode = [
|
||||
// "513030199509084123",
|
||||
// "340827200404141028",
|
||||
// "540421196805217650",
|
||||
// "540422200010197030",
|
||||
// "513425199305205211"
|
||||
// ];
|
||||
let roleData = getItem("roleList");
|
||||
// let deptData = getItem("deptId");
|
||||
roleData.forEach((item) => {
|
||||
if (rolCode.includes(item.roleCode)) hasPermissin.value = true;
|
||||
});
|
||||
// deptData.forEach((item) => {
|
||||
// if (depCode.includes(item.deptCode)) hasPermissin.value = true;
|
||||
// });
|
||||
if (hasPermissin.value) {
|
||||
const data = butList.value.filter(item => item !== "预警整合")
|
||||
const data = butList.value.filter((item) => item !== "预警整合");
|
||||
// "身份预警", "行为预警", "组合预警"
|
||||
butList.value = [...data, ...["标签预警", "预警整合"]]
|
||||
butList.value = [...data, ...["标签预警", "预警整合"]];
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">身份预警{{ title }} </span>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="small" :loading="loading" @click="submit">保存</el-button> -->
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form_cnt">
|
||||
<FormMessage :formList="formData" disabled v-model="listQuery" ref="elform">
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
|
||||
import { ref, defineExpose, reactive, defineEmits, getCurrentInstance, watch } from "vue";
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const dialogForm = ref(false); //弹窗
|
||||
const formData = ref([])
|
||||
watch(() => props.dict, (res) => {
|
||||
if (res) {
|
||||
formData.value = [
|
||||
{ label: "预警人员姓名", prop: "yjRyxm", type: "input" },
|
||||
{ label: "预警人员身份证号码", prop: "yjRysfzh", type: "input" },
|
||||
{ label: "电话", prop: "dh", type: "input" },
|
||||
{ label: "预警标签", prop: "yjbqmc", type: "input" },
|
||||
{ label: "今日预警次数", prop: "yjJrcs", type: "input",lx:"number" },
|
||||
{ label: "标签颜色", prop: "yjJb", type: "select", options: props.dict.D_GS_SSYJ },
|
||||
{ label: "预警标题", prop: "yjBt", type: "input" },
|
||||
{ label: "预警内容", prop: "yjNr", type: "input" },
|
||||
{ label: "预警时间", prop: "yjFssj", type: "input" },
|
||||
{ label: "处置状态", prop: "czzt", type: "select", options: props.dict.D_GSXT_YJXX_CZZT },
|
||||
{ label: "所属部门", prop: "ssbm", type: "input" },
|
||||
{ label: "所属县局", prop: "ssxgaj", type: "input" },
|
||||
{ label: "所属市局", prop: "sssgaj", type: "input" },
|
||||
]
|
||||
}
|
||||
}, { deep: true, immediate: true })
|
||||
const listQuery = ref({}); //表单
|
||||
const loading = ref(false);
|
||||
const title = ref("详情");
|
||||
const init = (type, row) => {
|
||||
dialogForm.value = true;
|
||||
qcckGet({},'/mosty-gsxt/tbYjxx/getInfo/'+ row.id).then((res) => {
|
||||
listQuery.value = res || {}
|
||||
})
|
||||
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
listQuery.value = {};
|
||||
loading.value = false;
|
||||
dialogForm.value = false;
|
||||
listQuery.value = {}
|
||||
};
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/css/layout.scss";
|
||||
@import "~@/assets/css/element-plus.scss";
|
||||
::v-deep {
|
||||
.el-form-item__content {
|
||||
align-items: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<el-dialog title="处置建议" v-model="visible" width="50%" v-if="visible" @close="closeHndle">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="120px" >
|
||||
<el-form-item label="处置建议" prop="jynr">
|
||||
<el-input v-model="form.jynr" placeholder="请输入处置建议" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
<div class="flex just-center">
|
||||
<el-button type="primary" @click="okSubmit">确定</el-button>
|
||||
<el-button @click="closeHndle">返回</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { qcckPost } from "@/api/qcckApi.js";
|
||||
import { ref , defineExpose} from 'vue'
|
||||
const emit = defineEmits(['okSubmit'])
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
const form = ref({})
|
||||
const rules = ref({
|
||||
jynr: [
|
||||
{ required: true, message: '请输入处置建议', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
const init = (row) => {
|
||||
visible.value = true;
|
||||
form.value.yjid = row.id;
|
||||
}
|
||||
|
||||
const closeHndle = () => {
|
||||
visible.value = false;
|
||||
form.value = {};
|
||||
}
|
||||
|
||||
const okSubmit = async () => {
|
||||
await formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
let params = {...form.value , lylx:'01'}
|
||||
qcckPost(params,'/mosty-gsxt/yjxx/czjy/insert').then((res) => {
|
||||
emit('okSubmit', {...form.value})
|
||||
closeHndle()
|
||||
})
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
init
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,478 @@
|
||||
<!--预警指派展示组件 -->
|
||||
<template>
|
||||
<el-dialog :draggable="true" :model-value="modelValue" :title="title" :width="width" @close="close" append-to-body>
|
||||
<div class="archive-container">
|
||||
<div class="three-column-layout">
|
||||
<!-- 重点人员基本信息页 -->
|
||||
<div class="column">
|
||||
<div class="column-header">重点人员基本信息</div>
|
||||
<div class="info-section">
|
||||
<div class="info-row">
|
||||
<div class="info-item">
|
||||
<span class="info-label">人员姓名:</span>
|
||||
<span class="info-value">{{ dataForm.yjRyxm }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">性别:</span>
|
||||
<span class="info-value">
|
||||
<DictTag :value="dataForm.xbdm" :tag="false" :options="dict.D_BZ_XB" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">年龄:</span>
|
||||
<span class="info-value">{{ dataForm.nl }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">身份证号码:</span>
|
||||
<span class="info-value">{{ dataForm.yjRysfzh }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">户籍地:</span>
|
||||
<span class="info-value">{{ dataForm.hjdXz }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">现住地址:</span>
|
||||
<span class="info-value">{{ dataForm.xzdXz }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">处置要求:</span>
|
||||
<span class="info-value">
|
||||
<DictTag :value="dataForm.bkczyq" :tag="false" :options="dict.D_GS_BK_CZYQ" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">预警级别:</span>
|
||||
<span class="info-value warning-level" :class="ys()">
|
||||
<DictTag :value="dataForm.yjJb" :tag="false" :options="dict.D_BZ_YJJB" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">布控起始时间:</span>
|
||||
<span class="info-value">{{ dataForm.bkkssj }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">布控结束时间:</span>
|
||||
<span class="info-value">{{ dataForm.bkjssj }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">所属单位:</span>
|
||||
<span class="info-value">{{ dataForm.ssbm }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">布控单位:</span>
|
||||
<span class="info-value">{{ dataForm.gkbmmc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">布控原因:</span>
|
||||
<span class="info-value text-area"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">一标三实:</span>
|
||||
<span class="info-value text-area"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 重点人员活动信息页 -->
|
||||
<div class="column">
|
||||
<div class="column-header">重点人员活动信息</div>
|
||||
<div class="info-section blue-bg">
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">轨迹类别:</span>
|
||||
<span class="info-value">
|
||||
<DictTag :value="dataForm.yjLylx" :tag="false" :options="dict.D_GS_ZDR_GJLB" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">核查时间:</span>
|
||||
<span v-if="dataForm.fkList && dataForm.fkList.length > 0">{{ dataForm.fkList[0].czsj }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">比对时间:</span>
|
||||
<span class="info-value">{{ dataForm.yjSj }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">预警内容:</span>
|
||||
<span class="info-value text-area">{{ dataForm.yjNr }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">预警信息提供单位:</span>
|
||||
<span class="info-value">{{ dataForm.jczmc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">预警信息接收单位:</span>
|
||||
<span class="info-value">{{ dataForm.ssbm }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">二次指派单位:</span>
|
||||
<span class="info-value">
|
||||
<div v-if="dataForm.zpList && dataForm.zpList.length > 0">
|
||||
<span v-for="(item, index) in dataForm.zpList" :key="item.id">
|
||||
{{ item.zpbm }}<span v-if="index < dataForm.zpList.length - 1">,</span>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">签收时限:</span>
|
||||
<span class="info-value">{{ dataForm.qssj }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">反馈时限:</span>
|
||||
<span class="info-value">{{ dataForm.fksj }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">处置建议:</span>
|
||||
<span class="info-value text-area">
|
||||
<div v-if="dataForm.czjyList && dataForm.czjyList.length > 0">
|
||||
<div v-for="(item, index) in dataForm.czjyList" :key="item.id">
|
||||
<span>{{ `${index + 1}、` + item.jynr }}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item full-width">
|
||||
<span class="info-label">反馈内容:</span>
|
||||
<span class="info-value text-area">
|
||||
<div v-if="dataForm.fkList && dataForm.fkList.length > 0">
|
||||
<div v-for="(item, index) in dataForm.fkList" :key="item.id">
|
||||
<span v-if="item.ckczbcxx">{{ `${index + 1}、` + item.ckczbcxx }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 历史预警信息页 -->
|
||||
<div class="column">
|
||||
<div class="column-header">历史预警信息</div>
|
||||
<div class="info-section">
|
||||
<div class="history-item" v-for="(item, index) in dataForm.yjgjList" :key="item.id">
|
||||
<span class="history-index">{{ `${index + 1}:` }}</span>
|
||||
<span class="history-content">{{ item.yjNr }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer" style="text-align: center;">
|
||||
<el-button @click="close">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineProps, getCurrentInstance, watch, ref } from 'vue';
|
||||
import { qcckGet } from '@/api/qcckApi.js'
|
||||
const { proxy } = getCurrentInstance();
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '预警详情'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '90%'
|
||||
}, dataList: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
|
||||
});
|
||||
// 定义事件
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const loading = ref(false)
|
||||
let abortController = null
|
||||
const close = () => {
|
||||
if (abortController) {
|
||||
abortController.abort()
|
||||
abortController = null
|
||||
}
|
||||
loading.value = false
|
||||
emit('update:modelValue', false);
|
||||
};
|
||||
const dataForm = ref({});
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (newVal) {
|
||||
getPart(props.dataList.id)
|
||||
}
|
||||
}, { deep: true });
|
||||
const getPart = (id) => {
|
||||
if (abortController) {
|
||||
abortController.abort()
|
||||
}
|
||||
abortController = new AbortController()
|
||||
loading.value = true
|
||||
qcckGet({}, `/mosty-gsxt/tbYjxx/getInfo/${id}`, { signal: abortController.signal }).then(res => {
|
||||
if (res) {
|
||||
dataForm.value = res
|
||||
} else {
|
||||
dataForm.value = {}
|
||||
}
|
||||
}).catch(err => {
|
||||
if (err.name !== 'AbortError') {
|
||||
console.error('请求失败:', err)
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const ys = () => {
|
||||
switch (dataForm.value.yjJb) {
|
||||
case '01':
|
||||
return 'red';
|
||||
case '02':
|
||||
return 'orange';
|
||||
case '03':
|
||||
return 'yellow';
|
||||
case '04':
|
||||
return 'blue';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.archive-container {
|
||||
padding: 0;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.three-column-layout {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 300px;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.column:not(:last-child) {
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.column-header {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
background: #f5f7fa;
|
||||
color: #303133;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.column .info-section {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.info-section.blue-bg {
|
||||
background: #e6f7ff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-item.full-width {
|
||||
flex: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: 600;
|
||||
margin-right: 10px;
|
||||
white-space: nowrap;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
width: 100px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #dcdfe6;
|
||||
background: #ffffff;
|
||||
min-height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.info-value.text-area {
|
||||
min-height: 80px;
|
||||
align-items: flex-start;
|
||||
padding: 8px;
|
||||
resize: vertical;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info-value.warning-level {
|
||||
background: #fffbe6;
|
||||
border-color: #ffe58f;
|
||||
color: #d48806;
|
||||
font-weight: 600;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.info-value.warning-level.red {
|
||||
background: #fef0f0;
|
||||
border-color: #ffccc7;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.info-value.warning-level.orange {
|
||||
background: #fffbe6;
|
||||
border-color: #ffe58f;
|
||||
color: #d48806;
|
||||
}
|
||||
|
||||
.info-value.warning-level.yellow {
|
||||
background: #fdf6ec;
|
||||
border-color: #faecd8;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.info-value.warning-level.blue {
|
||||
background: #ecf5ff;
|
||||
border-color: #d9ecff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
margin-bottom: 8px;
|
||||
padding: 6px 10px;
|
||||
border: none;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.history-index {
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
flex: 1;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.archive-container::-webkit-scrollbar,
|
||||
.info-section::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.archive-container::-webkit-scrollbar-track,
|
||||
.info-section::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.archive-container::-webkit-scrollbar-thumb,
|
||||
.info-section::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
}
|
||||
|
||||
.archive-container::-webkit-scrollbar-thumb:hover,
|
||||
.info-section::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* 按钮样式 */
|
||||
.dialog-footer {
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">身份预警{{ title }} </span>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="small" :loading="loading" @click="submit">保存</el-button> -->
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form_cnt">
|
||||
<FormMessage :formList="formData" v-model="listQuery" ref="elform">
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { tbYjxxGetInfo,yjzxyjzxSfyjSelectList } from "@/api/yj.js";
|
||||
import { IdCard } from '@/utils/validate.js'
|
||||
import { ref, defineExpose, reactive, defineEmits, getCurrentInstance, watch } from "vue";
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const dialogForm = ref(false); //弹窗
|
||||
const formData = ref([])
|
||||
watch(() => props.dict, (res) => {
|
||||
if (res) {
|
||||
formData.value = [
|
||||
{ label: "预警人姓名", prop: "xm", type: "input" },
|
||||
{ label: "身份证号", prop: "sfzh", type: "input" },
|
||||
{ label: "电话", prop: "dh", type: "input" },
|
||||
{ label: "组合大类", prop: "sfdlmc", type: "input" },
|
||||
{ label: "组合小类", prop: "sfzlmc", type: "input" },
|
||||
{ label: "组合预警次数", prop: "sfcs", type: "input",lx:"number" },
|
||||
{ label: "标签颜色", prop: "bqys", type: "select", options: props.dict.D_GS_SSYJ },
|
||||
{ label: "预警时间", prop: "yjsj", type: "input" },
|
||||
{ label: "预警分值", prop: "sffz", type: "input",lx:"number" },
|
||||
{ label: "处置状态", prop: "czzt", type: "select", options: props.dict.D_GSXT_YJXX_CZZT },
|
||||
{ label: "所属部门", prop: "ssbm", type: "input" },
|
||||
{ label: "所属县局", prop: "ssxgaj", type: "input" },
|
||||
{ label: "所属市局", prop: "sssgaj", type: "input" },
|
||||
{ label: "接警员姓名", prop: "jjyxm", type: "input" },
|
||||
]
|
||||
}
|
||||
|
||||
}, { deep: true, immediate: true })
|
||||
const listQuery = ref({}); //表单
|
||||
const loading = ref(false);
|
||||
const elform = ref();
|
||||
const title = ref("详情");
|
||||
const init = (type, row) => {
|
||||
dialogForm.value = true;
|
||||
yjzxyjzxSfyjSelectList(row.id).then(res => {
|
||||
listQuery.value = {
|
||||
...res,
|
||||
nl: IdCard(res.yjRysfzh, 3) || "",
|
||||
xb: IdCard(res.yjRysfzh, 2) || "",
|
||||
xsd: res.xsd + '%'
|
||||
}
|
||||
})
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
listQuery.value = {};
|
||||
loading.value = false;
|
||||
dialogForm.value = false;
|
||||
listQuery.value = {}
|
||||
};
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/css/layout.scss";
|
||||
@import "~@/assets/css/element-plus.scss";
|
||||
::v-deep {
|
||||
.el-form-item__content {
|
||||
align-items: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">行为预警{{ title }} </span>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="small" :loading="loading" @click="submit">保存</el-button> -->
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form_cnt">
|
||||
<FormMessage :formList="formData" v-model="listQuery" ref="elform">
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { tbYjxxGetInfo,yjzxXwyjSelectList } from "@/api/yj.js";
|
||||
import { IdCard } from '@/utils/validate.js'
|
||||
import { ref, defineExpose, reactive, defineEmits, getCurrentInstance, watch } from "vue";
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const dialogForm = ref(false); //弹窗
|
||||
const formData = ref([])
|
||||
watch(() => props.dict, (res) => {
|
||||
if (res) {
|
||||
formData.value = [
|
||||
{ label: "预警人姓名", prop: "xm", type: "input" },
|
||||
{ label: "身份证号", prop: "sfzh", type: "input" },
|
||||
{ label: "电话", prop: "dh", type: "input" },
|
||||
{ label: "行为大类", prop: "xldlmc", type: "input" },
|
||||
{ label: "行为子类", prop: "xwzlmc", type: "input" },
|
||||
{ label: "行为次数", prop: "xwcs", type: "input",lx:"number" },
|
||||
{ label: "标签颜色", prop: "bqys", type: "select", options: props.dict.D_GS_SSYJ },
|
||||
{ label: "预警时间", prop: "yjsj", type: "input" },
|
||||
{ label: "行为分值", prop: "xwfz", type: "input",lx:"number" },
|
||||
{ label: "处置状态", prop: "czzt", type: "select", options: props.dict.D_GSXT_YJXX_CZZT },
|
||||
{ label: "所属部门", prop: "ssbm", type: "input" },
|
||||
{ label: "所属县局", prop: "ssxgaj", type: "input" },
|
||||
{ label: "所属市局", prop: "sssgaj", type: "input" },
|
||||
{ label: "接警员姓名", prop: "jjyxm", type: "input" },
|
||||
{ label: "行为描述", prop: "xwms", type: "textarea", width: "100%" },
|
||||
]
|
||||
}
|
||||
|
||||
}, { deep: true, immediate: true })
|
||||
const listQuery = ref({}); //表单
|
||||
const loading = ref(false);
|
||||
const elform = ref();
|
||||
const title = ref("详情");
|
||||
const init = (row) => {
|
||||
dialogForm.value = true;
|
||||
yjzxXwyjSelectList(row.id).then(res => {
|
||||
listQuery.value = {
|
||||
...res,
|
||||
nl: IdCard(res.yjRysfzh, 3) || "",
|
||||
xb: IdCard(res.yjRysfzh, 2) || "",
|
||||
xsd: res.xsd + '%'
|
||||
}
|
||||
})
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
listQuery.value = {};
|
||||
loading.value = false;
|
||||
dialogForm.value = false;
|
||||
listQuery.value = {}
|
||||
};
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/css/layout.scss";
|
||||
@import "~@/assets/css/element-plus.scss";
|
||||
::v-deep {
|
||||
.el-form-item__content {
|
||||
align-items: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,547 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 搜索 -->
|
||||
<div ref="searchBox" class="mt10">
|
||||
<QueryFormPanel
|
||||
v-model="listQuery"
|
||||
:fields="searchConfiger"
|
||||
@search="onSearch"
|
||||
>
|
||||
<template #but>
|
||||
<el-button type="primary" @click="exportExl" size="small"
|
||||
>导出</el-button
|
||||
>
|
||||
<el-button type="primary" size="small" @click="handleQs"
|
||||
>签收</el-button
|
||||
>
|
||||
</template>
|
||||
</QueryFormPanel>
|
||||
</div>
|
||||
<!-- 表格 -->
|
||||
<div
|
||||
class="tabBox_zdy"
|
||||
:style="{ height: pageData.tableHeight + 40 + 'px' }"
|
||||
>
|
||||
<WarnDataTable
|
||||
:loading="pageData.tableConfiger.loading"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:data="pageData.tableData"
|
||||
:columns="pageData.tableColumn"
|
||||
table-class="warn-table"
|
||||
@selectionChange="handleChooseData"
|
||||
>
|
||||
<template #czzt="{ row }">
|
||||
<DictTag
|
||||
:value="row.czzt"
|
||||
:color="row.czzt === '01' ? '#ff2424' : '#1d72e8'"
|
||||
:tag="false"
|
||||
:options="D_GSXT_YJXX_CZZT"
|
||||
/>
|
||||
</template>
|
||||
<template #xbdm="{ row }">
|
||||
<DictTag :value="row.xbdm" :tag="false" :options="D_BZ_XB" />
|
||||
</template>
|
||||
<template #yjJb="{ row }">
|
||||
<div :style="{ 'background-color': bqYs(row.yjJb) }">
|
||||
<DictTag
|
||||
:value="row.yjJb"
|
||||
color="#fff"
|
||||
:tag="false"
|
||||
:options="D_BZ_YJJB"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #qblyjb="{ row }">
|
||||
<DictTag :value="row.qblyjb" :tag="false" :options="D_BZ_QBLYJB" />
|
||||
</template>
|
||||
|
||||
<template #bksj="{ row }">
|
||||
{{ row.bkkssj && row.bkjssj ? `${row.bkkssj} - ${row.bkjssj}` : "" }}
|
||||
</template>
|
||||
<template #xsd="{ row }"> {{ row.xsd }}% </template>
|
||||
<template #cszt="{ row }">
|
||||
<DictTag :value="row.cszt" :tag="false" :options="D_GS_CSZT" />
|
||||
</template>
|
||||
|
||||
<template #bkyjlx="{ row }">
|
||||
<DictTag :value="row.bkyjlx" :tag="false" :options="D_BZ_BKYJLX" />
|
||||
</template>
|
||||
|
||||
<template #operation="{ row }">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<span class="warning" @click="pushAssess(row)">全息档案</span>
|
||||
<span class="primary" @click="handleCzjy(row)" v-if="roleCode"
|
||||
>处置建议</span
|
||||
>
|
||||
<!-- <span type="primary" @click="showDetail(row)">转合成</span> -->
|
||||
<!-- <span type="danger" @click="delDictItem(row.id)">转会商</span> -->
|
||||
<span
|
||||
class="success"
|
||||
@click="handleQsFk(row, '签收')"
|
||||
v-if="row.czzt == '01' && permission_sfqs"
|
||||
>签收</span
|
||||
>
|
||||
<span
|
||||
class="success"
|
||||
@click="handleQsFk(row, '反馈')"
|
||||
v-else-if="row.czzt == '02' && permission_sfqs"
|
||||
>反馈</span
|
||||
>
|
||||
<!-- <span type="success" @click="handleQsFk(row, '查看反馈')" v-else>查看反馈</span> -->
|
||||
<span class="primary" @click="openBox(row)">详情</span>
|
||||
<span class="primary" @click="pushWarning(row)">指派</span>
|
||||
</div>
|
||||
</template>
|
||||
</WarnDataTable>
|
||||
<Pages
|
||||
@changeNo="changeNo"
|
||||
@changeSize="changeSize"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"
|
||||
></Pages>
|
||||
</div>
|
||||
</div>
|
||||
<FkDialog @change="getList" lx="05" />
|
||||
<AddFrom
|
||||
ref="addModelRef"
|
||||
:dict="{ D_GSXT_YJXX_CZZT, D_BZ_YJJB, D_GS_SSYJ }"
|
||||
/>
|
||||
<!-- 处置建议 -->
|
||||
<Czjy ref="czjyRef" @okSubmit="getList"></Czjy>
|
||||
<ZpForm v-model="warningShow" :dataList="dataList" />
|
||||
<!-- <Pagination v-model="paginationOpen" /> -->
|
||||
<Pagination
|
||||
v-model="paginationOpen"
|
||||
:dataList="dataPres"
|
||||
:dict="{
|
||||
D_BZ_XB,
|
||||
D_BZ_YJJB,
|
||||
D_GS_QLZDRLX,
|
||||
D_GS_ZDR_RYJB,
|
||||
D_GS_ZDR_GJLB,
|
||||
D_GS_BK_CZYQ
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { IdCard } from "@/utils/validate.js";
|
||||
import Czjy from "./components/czjy.vue";
|
||||
import { getItem, setItem } from "@/utils/storage";
|
||||
import WarnDataTable from "@/views/backOfficeSystem/ces/components/WarnDataTable.vue";
|
||||
import QueryFormPanel from "@/views/backOfficeSystem/ces/components/QueryFormPanel.vue";
|
||||
import Pages from "@/components/aboutTable/Pages.vue";
|
||||
import AddFrom from "./components/addFrom.vue";
|
||||
import FkDialog from "@/views/backOfficeSystem/fourColorManage/warningControl/centerHome/components/fkDialog.vue";
|
||||
import ZpForm from "@/views/backOfficeSystem/fourColorManage/warningControl/sevenWarning/zpForm.vue";
|
||||
import { reactive, ref, onMounted, getCurrentInstance, nextTick } from "vue";
|
||||
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
|
||||
import emitter from "@/utils/eventBus.js";
|
||||
import { holographicProfileJump } from "@/utils/tools.js";
|
||||
import { exportExlByObj } from "@/utils/exportExcel.js";
|
||||
import { getMultiDictVal } from "@/utils/dict.js";
|
||||
import Pagination from "./components/particulars.vue";
|
||||
const czjyRef = ref();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const searchBox = ref();
|
||||
const {
|
||||
D_GS_QLZDRLX,
|
||||
D_BZ_YJLY,
|
||||
D_GSXT_YJXX_CZZT,
|
||||
D_GS_SSYJ,
|
||||
D_BZ_YJJB,
|
||||
D_BZ_BKLYS,
|
||||
D_BZ_XB,
|
||||
D_BZ_SF,
|
||||
D_GS_CSZT,
|
||||
D_GS_BKZT,
|
||||
D_GS_ZDR_RYJB,
|
||||
D_GS_ZDR_GJLB,
|
||||
D_GS_BK_CZYQ,
|
||||
D_BZ_SJLY,
|
||||
D_BZ_QBLYJB,
|
||||
D_BZ_BKYJLX
|
||||
} = proxy.$dict(
|
||||
"D_GS_QLZDRLX",
|
||||
"D_BZ_YJLY",
|
||||
"D_GSXT_YJXX_CZZT",
|
||||
"D_GS_SSYJ",
|
||||
"D_BZ_YJJB",
|
||||
"D_BZ_BKLYS",
|
||||
"D_BZ_XB",
|
||||
"D_BZ_SF",
|
||||
"D_GS_CSZT",
|
||||
"D_GS_BKZT",
|
||||
"D_GS_ZDR_RYJB",
|
||||
"D_GS_ZDR_GJLB",
|
||||
"D_GS_BK_CZYQ",
|
||||
"D_BZ_SJLY",
|
||||
"D_BZ_QBLYJB",
|
||||
"D_BZ_BKYJLX"
|
||||
);
|
||||
const dict = reactive({ D_GSXT_YJXX_CZZT, D_GS_SSYJ });
|
||||
// 搜索配置
|
||||
const searchConfiger = ref([
|
||||
{
|
||||
label: "处置状态",
|
||||
prop: "czzt",
|
||||
showType: "select",
|
||||
options: D_GSXT_YJXX_CZZT
|
||||
},
|
||||
{
|
||||
label: "预警时间",
|
||||
prop: "startTime",
|
||||
showType: "datetimerange",
|
||||
placeholder: "请选择预警时间"
|
||||
},
|
||||
{
|
||||
label: "姓名",
|
||||
prop: "yjRyxm",
|
||||
showType: "input",
|
||||
placeholder: "请输入姓名"
|
||||
},
|
||||
{
|
||||
label: "身份证号",
|
||||
prop: "yjRysfzh",
|
||||
showType: "input",
|
||||
placeholder: "请输入身份证号"
|
||||
},
|
||||
{
|
||||
label: "管控级别",
|
||||
prop: "qblyjb",
|
||||
showType: "select",
|
||||
options: D_BZ_QBLYJB
|
||||
},
|
||||
{
|
||||
label: "预警级别",
|
||||
prop: "yjJb",
|
||||
showType: "select",
|
||||
options: D_BZ_YJJB,
|
||||
placeholder: "请选择预警级别",
|
||||
multiple: true
|
||||
},
|
||||
{
|
||||
label: "布控时间",
|
||||
prop: "bksj",
|
||||
showType: "daterange",
|
||||
placeholder: "请选择布控开始时间"
|
||||
},
|
||||
{
|
||||
label: "接收单位",
|
||||
prop: "ssbmdm",
|
||||
showType: "department",
|
||||
placeholder: "请选择接收单位"
|
||||
},
|
||||
{
|
||||
label: "布控单位",
|
||||
prop: "gkbmdm",
|
||||
showType: "department",
|
||||
placeholder: "请选择布控单位"
|
||||
},
|
||||
|
||||
{
|
||||
label: "比对源",
|
||||
prop: "bkyjlx",
|
||||
showType: "select",
|
||||
options: D_BZ_BKYJLX,
|
||||
placeholder: "请选择比对源"
|
||||
},
|
||||
{
|
||||
label: "超时状态",
|
||||
prop: "cszt",
|
||||
placeholder: "请选择超时状态",
|
||||
showType: "select",
|
||||
options: D_GS_CSZT
|
||||
},
|
||||
{
|
||||
label: "在控状态",
|
||||
prop: "zkzt",
|
||||
showType: "select",
|
||||
options: D_GS_BKZT,
|
||||
placeholder: "请选择在控状态"
|
||||
}
|
||||
]);
|
||||
const ORDIMG = "https://89.40.7.122:38496/image";
|
||||
const IMGYM = "https://sg.lz.dsj.xz/dhimage";
|
||||
const permission_sfqs = ref(false);
|
||||
const roleCode = ref(false);
|
||||
|
||||
const queryFrom = ref({});
|
||||
|
||||
// 页面数据
|
||||
const pageData = reactive({
|
||||
tableData: [],
|
||||
keyCount: 0,
|
||||
tableConfiger: {
|
||||
rowHieght: 61,
|
||||
showSelectType: "checkBox",
|
||||
loading: false,
|
||||
haveControls: true
|
||||
},
|
||||
total: 0,
|
||||
pageConfiger: {
|
||||
pageSize: 20,
|
||||
pageCurrent: 1
|
||||
},
|
||||
controlsWidth: 180, //操作栏宽度
|
||||
tableColumn: [
|
||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||
{ label: "处置状态", align: "center", width: 70, slotName: "czzt" },
|
||||
{ prop: "yjSj", label: "预警时间", width: 150 },
|
||||
{ prop: "yjRyxm", label: "人员姓名", width: 70, align: "center" },
|
||||
{ label: "性别", width: 50, align: "center", slotName: "xbdm" },
|
||||
{ prop: "nl", label: "年龄", width: 50, align: "center" },
|
||||
{ prop: "yjRysfzh", label: "身份证号", width: 150, align: "center" },
|
||||
{ label: "管控级别", width: 80, align: "center", slotName: "qblyjb" },
|
||||
{ label: "预警级别", width: 80, align: "center", slotName: "yjJb" },
|
||||
{ label: "布控时间", width: 80, align: "center", slotName: "bksj" },
|
||||
{ prop: "ssbm", label: "接收单位", width: 100, align: "center" },
|
||||
{ label: "布控单位", prop: "gkbmmc", align: "center", width: 100 },
|
||||
{ prop: "bkyjlx", label: "比对源", align: "center", width: 80 },
|
||||
{ label: "相似度", slotName: "xsd", align: "center", width: 50 },
|
||||
{ label: "预警内容", prop: "yjNr", align: "center" },
|
||||
{ label: "操作", width: 180, slotName: "operation" },
|
||||
{ label: "超时状态", width: 80, align: "center", slotName: "cszt" },
|
||||
{ label: "在控状态", width: 70, align: "center", slotName: "zkzt" }
|
||||
// { prop: "bkkssj", label: "布控开始时间", align: "center" },
|
||||
// { prop: "bkjssj", label: "布控结束时间", align: "center" }
|
||||
//
|
||||
// { label: "预警级别", width: 80, align: "center", slotName: "yjJb" },
|
||||
//
|
||||
// { label: "布控来源", align: "center", slotName: "bkly", width: 70 },
|
||||
// { prop: "bkfw", label: "布控范围", align: "center", width: 70 },
|
||||
// { label: "处置要求", width: 70, slotName: "bkczyq", align: "center" },
|
||||
// { label: "预警内容", prop: "yjNr", align: "center" },
|
||||
// { label: "相似度", slotName: "xsd", align: "center", width: 50 },
|
||||
// { label: "所属部门", prop: "ssbm", align: "center" },
|
||||
// { label: "数据来源", slotName: "yjLylx", align: "center" },
|
||||
]
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
let str = getItem("deptId") ? getItem("deptId")[0].deptLevel : "";
|
||||
permission_sfqs.value = str.startsWith("2" || "3") ? false : true;
|
||||
let rols = getItem("roleList") ? getItem("roleList") : [];
|
||||
let obj = rols.find((item) => {
|
||||
return ["JS_666666", "JS_777777", "JS_888888"].includes(item.roleCode);
|
||||
});
|
||||
roleCode.value = obj ? true : false;
|
||||
|
||||
tabHeightFn();
|
||||
getList();
|
||||
});
|
||||
|
||||
const onSearch = (val) => {
|
||||
queryFrom.value = {
|
||||
...queryFrom.value,
|
||||
...val,
|
||||
startTime: val.startTime ? val.startTime[0] : "",
|
||||
endTime: val.startTime ? val.startTime[1] : "",
|
||||
bkkssj: val.bksj ? val.bksj[0] : "",
|
||||
bkjssj: val.bksj ? val.bksj[1] : "",
|
||||
yjJb: val.yjJb?.join(",") || ""
|
||||
};
|
||||
|
||||
pageData.pageConfiger.pageCurrent = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
delete queryFrom.value.ksfz;
|
||||
delete queryFrom.value.jsfz;
|
||||
};
|
||||
|
||||
const changeNo = (val) => {
|
||||
pageData.pageConfiger.pageCurrent = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const changeSize = (val) => {
|
||||
pageData.pageConfiger.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const getList = () => {
|
||||
pageData.tableConfiger.loading = true;
|
||||
let params = {
|
||||
...queryFrom.value,
|
||||
pageCurrent: pageData.pageConfiger.pageCurrent,
|
||||
pageSize: pageData.pageConfiger.pageSize,
|
||||
yjlb: "01"
|
||||
};
|
||||
qcckPost(params, "/mosty-gsxt/tbYjxx/getPageList")
|
||||
.then((res) => {
|
||||
pageData.tableData = res?.records || [];
|
||||
pageData.total = res?.total || 0;
|
||||
pageData.tableConfiger.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 处理签收
|
||||
const handleQsFk = (val, type) => {
|
||||
switch (type) {
|
||||
case "签收":
|
||||
proxy
|
||||
.$confirm("是否确定要签收?", "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
qcckPost({ id: val.id }, "/mosty-gsxt/mosty-gsxt/tbYjxx/yjqs").then(
|
||||
() => {
|
||||
val.czzt = "02";
|
||||
getList();
|
||||
proxy.$message({ type: "success", message: "签收成功" });
|
||||
}
|
||||
);
|
||||
});
|
||||
break;
|
||||
case "反馈":
|
||||
case "查看反馈":
|
||||
emitter.emit("openFkDialog", { id: val.id, type });
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const pushAssess = (val) => {
|
||||
return holographicProfileJump(val?.yjLx, val); // 全息档案跳转
|
||||
};
|
||||
|
||||
const bqYs = (val) => {
|
||||
switch (val) {
|
||||
case "01":
|
||||
return "#ff0202";
|
||||
case "02":
|
||||
return "#ff8c00";
|
||||
case "03":
|
||||
return "#bdbd00";
|
||||
case "04":
|
||||
return "#0000ff";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
// 新增
|
||||
const addModelRef = ref(null);
|
||||
const openAddFrom = (row) => {
|
||||
addModelRef.value.init("add", row);
|
||||
};
|
||||
|
||||
const handleCzjy = (row) => {
|
||||
czjyRef.value.init(row);
|
||||
};
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
pageData.tableHeight =
|
||||
window.innerHeight - searchBox.value.offsetHeight - 230;
|
||||
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
// 指派
|
||||
const dataList = ref(null);
|
||||
const warningShow = ref(false);
|
||||
const pushWarning = (val) => {
|
||||
warningShow.value = true;
|
||||
dataList.value = val;
|
||||
};
|
||||
|
||||
/** 选中项 */
|
||||
const selectRows = ref([]);
|
||||
const handleChooseData = (val) => {
|
||||
selectRows.value = val;
|
||||
};
|
||||
const exportExl = () => {
|
||||
const titleObj = {
|
||||
czzt_cname: "处置状态",
|
||||
yjSj: "预警时间",
|
||||
yjRyxm: "姓名",
|
||||
nl_cname: "年龄", // IdCard(row.yjRysfzh, 3)
|
||||
yjLylx: "数据来源",
|
||||
xb_cname: "性别",
|
||||
yjJb_cname: "预警级别",
|
||||
xsd_cname: "相似度",
|
||||
yjDz: "预警地点",
|
||||
yjCs: "预警次数",
|
||||
yjRysjh: "布控手机号",
|
||||
yjClcph: "布控车牌号",
|
||||
yjRysfzh: "身份证"
|
||||
};
|
||||
/** 导出【选中】的数据 (没有就全部)*/
|
||||
const needArr =
|
||||
selectRows.value?.length > 0 ? selectRows.value : pageData.tableData;
|
||||
const data = needArr.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
nl_cname: IdCard(item.yjRysfzh, 3),
|
||||
xb_cname: IdCard(item.yjRysfzh, 2),
|
||||
xsd_cname: (item.xsd > 0 ? item.xsd : 0) + "%",
|
||||
yjJb_name: getMultiDictVal(item.yjJb, D_GS_SSYJ),
|
||||
czzt_name: getMultiDictVal(item.czzt, D_GSXT_YJXX_CZZT),
|
||||
yjJb_cname: getMultiDictVal(item.yjJb, D_BZ_YJJB)
|
||||
};
|
||||
});
|
||||
exportExlByObj(titleObj, data, "预警布控");
|
||||
};
|
||||
|
||||
const handleQs = () => {
|
||||
if (selectRows.value?.length === 0)
|
||||
return proxy.$message({ type: "warning", message: "请选择要签收的预警" });
|
||||
let wqs = selectRows.value.filter((item) => item.czzt == "01");
|
||||
if (wqs.length == 0)
|
||||
return proxy.$message({
|
||||
type: "warning",
|
||||
message: "数据都已签收,请选择未签收的数据"
|
||||
});
|
||||
let yqs = selectRows.value.filter((item) => item.czzt == "02");
|
||||
let texy =
|
||||
yqs.length > 0
|
||||
? `${yqs.length}条已签收预警数据,确认要签收${wqs.length}条未签收预警数据吗?`
|
||||
: "确认要签收所有预警数据吗?";
|
||||
proxy
|
||||
.$confirm(texy, "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
let ids = wqs.map((item) => item.id);
|
||||
qcckPost({ ids }, "/mosty-gsxt/tbYjxx/batchQs")
|
||||
.then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 详情
|
||||
const paginationOpen = ref(false);
|
||||
const dataPres = ref({});
|
||||
const openBox = (val) => {
|
||||
paginationOpen.value = true;
|
||||
dataPres.value = val;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-loading-mask {
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
.tabBox_zdy {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
::v-deep .el-table .cell {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::v-deep .el-table .el-table__cell {
|
||||
padding: 4px 0;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="warning-item" >
|
||||
|
||||
<el-divider content-position="left">预警内容</el-divider>
|
||||
<div class="item-row" style="border: none;"> {{ props.row.yjNr }} </div>
|
||||
<el-empty v-if="!props.row.yjNr" :image-size="0.5" description="暂无数据" />
|
||||
|
||||
<el-divider content-position="left">处置建议</el-divider>
|
||||
<div class="item-row" v-for="(it,idx) in list" :key="idx">
|
||||
<div class="info-item">
|
||||
<span class="text">预警人姓名:{{ it.jryXm }}</span>
|
||||
<span class="text">建议时间:{{ it.jysj }}</span>
|
||||
<span class="text">所属部门:{{ it.ssbm }}</span>
|
||||
</div>
|
||||
<div class="info-item">建议内容:<span>{{ it.jynr || '暂无' }}</span></div>
|
||||
</div>
|
||||
<div>
|
||||
<el-empty v-if="list.length === 0" :image-size="0.5" description="暂无数据" />
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">反馈内容</el-divider>
|
||||
<div class="item-row" v-for="(it,idx) in Fklist" :key="idx">
|
||||
<div class="info-item">
|
||||
<span class="text">处置地址:{{ it.czdz }}</span>
|
||||
<span class="text">处置时间:{{ it.czsj }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="text">常控不尿检理由:<span>{{ it.ckbnjly || '暂无' }}</span></span>
|
||||
<span class="text">常控处置反馈补充信息:<span>{{ it.ckczbcxx || '暂无' }}</span></span>
|
||||
<span class="text">常控立线侦察评估:<span>{{ it.cklxzcpg || '暂无' }}</span></span>
|
||||
<span class="text">常控立线侦察评估依据:<span>{{ it.cklxzcpgyj || '暂无' }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-empty v-if="Fklist.length === 0" :image-size="0.5" description="暂无数据" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { qcckPost,qcckGet } from "@/api/qcckApi.js";
|
||||
|
||||
const props = defineProps({
|
||||
/** 表格行数据 */
|
||||
row: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
D_GS_SSYJ: [],
|
||||
D_GSXT_YJXX_CZZT: []
|
||||
})
|
||||
},
|
||||
})
|
||||
const list = ref([])
|
||||
const Fklist = ref([])
|
||||
onMounted(() => {
|
||||
if(!props.row.id) return;
|
||||
qcckPost({yjid: props.row.id},'/mosty-gsxt/yjxx/czjy/getPageList').then((res) => {
|
||||
list.value = res.records || []
|
||||
})
|
||||
qcckGet({},'/mosty-gsxt/tbYjxx/getInfo/'+ props.row.id).then((res) => {
|
||||
Fklist.value = res.fkList || []
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.warning-item {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.item-row{
|
||||
border-bottom: 1px dashed #e8e8e8;
|
||||
line-height: 36px;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 2rem;
|
||||
box-sizing: border-box;
|
||||
&:nth-last-child(1){
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
.info-item{
|
||||
line-height: 36px;
|
||||
width: 100%;
|
||||
.text{
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-empty{
|
||||
--el-empty-padding: 0px;
|
||||
margin-bottom: 18px;
|
||||
--el-empty-description-margin-top: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<el-dialog title="处置建议" v-model="visible" width="50%" v-if="visible" @close="closeHndle">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="120px" >
|
||||
<el-form-item label="处置建议" prop="jynr">
|
||||
<el-input v-model="form.jynr" placeholder="请输入处置建议" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
<div class="flex just-center">
|
||||
<el-button type="primary" @click="okSubmit">确定</el-button>
|
||||
<el-button @click="closeHndle">返回</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { qcckPost } from "@/api/qcckApi.js";
|
||||
import { ref , defineExpose} from 'vue'
|
||||
const emit = defineEmits(['okSubmit'])
|
||||
const visible = ref(false)
|
||||
const formRef = ref()
|
||||
const form = ref({})
|
||||
const rules = ref({
|
||||
jynr: [
|
||||
{ required: true, message: '请输入处置建议', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
const init = (row) => {
|
||||
visible.value = true;
|
||||
form.value.yjid = row.id;
|
||||
}
|
||||
|
||||
const closeHndle = () => {
|
||||
visible.value = false;
|
||||
form.value = {};
|
||||
}
|
||||
|
||||
const okSubmit = async () => {
|
||||
await formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
let params = {...form.value , lylx:'02'}
|
||||
qcckPost(params,'/mosty-gsxt/yjxx/czjy/insert').then((res) => {
|
||||
emit('okSubmit', {...form.value})
|
||||
closeHndle()
|
||||
})
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
init
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<el-dialog :title="`组合预警次数详情(${pageData.tableData.length})`" v-model="dialogVisible" width="60%">
|
||||
<MyTable
|
||||
:tableData="pageData.tableData"
|
||||
:tableColumn="pageData.tableColumn"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:key="pageData.keyCount"
|
||||
:tableConfiger="pageData.tableConfiger"
|
||||
>
|
||||
</MyTable>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { qcckGet } from "@/api/qcckApi.js";
|
||||
import MyTable from "@/components/aboutTable/MyTable.vue";
|
||||
import { ref , reactive , defineExpose} from 'vue'
|
||||
const dialogVisible = ref(false)
|
||||
const pageData = reactive({
|
||||
tableData: [{jqbh:'JQBH-43',bjr:'张三',bjrdh:'15665255545',bjrsfzh:'510156565656525565',yjnr:'xxxxxxxxx',jsj:'2023-08-24 15:00:00'}], //表格数据
|
||||
keyCount: 0,
|
||||
tableConfiger: {
|
||||
rowHieght: 61,
|
||||
loading: false,
|
||||
haveControls: false,
|
||||
},
|
||||
tableHeight:600,
|
||||
total: 0,
|
||||
pageConfiger: {
|
||||
pageSize: 20,
|
||||
pageCurrent: 1
|
||||
}, //分页
|
||||
tableColumn: [
|
||||
{ label: "警情编号", prop: "jjdbh"},
|
||||
{ label: "报警人", prop: "bjrmc" },
|
||||
{ label: "报警人电话", prop: "bjdh" },
|
||||
{ label: "报警人身份证", prop: "bjrzjhm"},
|
||||
{ label: "预警内容", prop: "bjnr", showOverflowTooltip: true },
|
||||
{ label: "报警时间", prop: "bjsj", showOverflowTooltip: true },
|
||||
]
|
||||
});
|
||||
const init = (row) => {
|
||||
dialogVisible.value = true;
|
||||
pageData.tableConfiger.loading = true;
|
||||
pageData.tableData = []
|
||||
qcckGet({yjid:row.id},'/mosty-gsxt/yjzxSfyjxq/selectList').then((res)=>{
|
||||
pageData.tableData = res || [];
|
||||
pageData.tableConfiger.loading = false;
|
||||
}).catch(()=>{
|
||||
pageData.tableConfiger.loading = false;
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
init
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">身份预警{{ title }} </span>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="small" :loading="loading" @click="submit">保存</el-button> -->
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form_cnt">
|
||||
<FormMessage :formList="formData" v-model="listQuery" ref="elform">
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { tbYjxxGetInfo,yjzxyjzxSfyjSelectList } from "@/api/yj.js";
|
||||
import { IdCard } from '@/utils/validate.js'
|
||||
import { ref, defineExpose, reactive, defineEmits, getCurrentInstance, watch } from "vue";
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const dialogForm = ref(false); //弹窗
|
||||
const formData = ref([])
|
||||
watch(() => props.dict, (res) => {
|
||||
if (res) {
|
||||
formData.value = [
|
||||
{ label: "预警人姓名", prop: "xm", type: "input" },
|
||||
{ label: "身份证号", prop: "sfzh", type: "input" },
|
||||
{ label: "电话", prop: "dh", type: "input" },
|
||||
{ label: "组合大类", prop: "sfdlmc", type: "input" },
|
||||
{ label: "组合小类", prop: "sfzlmc", type: "input" },
|
||||
{ label: "组合预警次数", prop: "sfcs", type: "input",lx:"number" },
|
||||
{ label: "标签颜色", prop: "bqys", type: "select", options: props.dict.D_GS_SSYJ },
|
||||
{ label: "预警时间", prop: "yjsj", type: "input" },
|
||||
{ label: "预警分值", prop: "sffz", type: "input",lx:"number" },
|
||||
{ label: "处置状态", prop: "czzt", type: "select", options: props.dict.D_GSXT_YJXX_CZZT },
|
||||
{ label: "所属部门", prop: "ssbm", type: "input" },
|
||||
{ label: "所属县局", prop: "ssxgaj", type: "input" },
|
||||
{ label: "所属市局", prop: "sssgaj", type: "input" },
|
||||
{ label: "接警员姓名", prop: "jjyxm", type: "input" },
|
||||
]
|
||||
}
|
||||
|
||||
}, { deep: true, immediate: true })
|
||||
const listQuery = ref({}); //表单
|
||||
const loading = ref(false);
|
||||
const elform = ref();
|
||||
const title = ref("详情");
|
||||
const init = (type, row) => {
|
||||
dialogForm.value = true;
|
||||
yjzxyjzxSfyjSelectList(row.id).then(res => {
|
||||
listQuery.value = {
|
||||
...res,
|
||||
nl: IdCard(res.yjRysfzh, 3) || "",
|
||||
xb: IdCard(res.yjRysfzh, 2) || "",
|
||||
xsd: res.xsd + '%'
|
||||
}
|
||||
})
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
listQuery.value = {};
|
||||
loading.value = false;
|
||||
dialogForm.value = false;
|
||||
listQuery.value = {}
|
||||
};
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/css/layout.scss";
|
||||
@import "~@/assets/css/element-plus.scss";
|
||||
::v-deep {
|
||||
.el-form-item__content {
|
||||
align-items: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">行为预警{{ title }} </span>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="small" :loading="loading" @click="submit">保存</el-button> -->
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form_cnt">
|
||||
<FormMessage :formList="formData" v-model="listQuery" ref="elform">
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { tbYjxxGetInfo,yjzxXwyjSelectList } from "@/api/yj.js";
|
||||
import { IdCard } from '@/utils/validate.js'
|
||||
import { ref, defineExpose, reactive, defineEmits, getCurrentInstance, watch } from "vue";
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const dialogForm = ref(false); //弹窗
|
||||
const formData = ref([])
|
||||
watch(() => props.dict, (res) => {
|
||||
if (res) {
|
||||
formData.value = [
|
||||
{ label: "预警人姓名", prop: "xm", type: "input" },
|
||||
{ label: "身份证号", prop: "sfzh", type: "input" },
|
||||
{ label: "电话", prop: "dh", type: "input" },
|
||||
{ label: "行为大类", prop: "xldlmc", type: "input" },
|
||||
{ label: "行为子类", prop: "xwzlmc", type: "input" },
|
||||
{ label: "行为次数", prop: "xwcs", type: "input",lx:"number" },
|
||||
{ label: "标签颜色", prop: "bqys", type: "select", options: props.dict.D_GS_SSYJ },
|
||||
{ label: "预警时间", prop: "yjsj", type: "input" },
|
||||
{ label: "行为分值", prop: "xwfz", type: "input",lx:"number" },
|
||||
{ label: "处置状态", prop: "czzt", type: "select", options: props.dict.D_GSXT_YJXX_CZZT },
|
||||
{ label: "所属部门", prop: "ssbm", type: "input" },
|
||||
{ label: "所属县局", prop: "ssxgaj", type: "input" },
|
||||
{ label: "所属市局", prop: "sssgaj", type: "input" },
|
||||
{ label: "接警员姓名", prop: "jjyxm", type: "input" },
|
||||
{ label: "行为描述", prop: "xwms", type: "textarea", width: "100%" },
|
||||
]
|
||||
}
|
||||
|
||||
}, { deep: true, immediate: true })
|
||||
const listQuery = ref({}); //表单
|
||||
const loading = ref(false);
|
||||
const elform = ref();
|
||||
const title = ref("详情");
|
||||
const init = (row) => {
|
||||
dialogForm.value = true;
|
||||
yjzxXwyjSelectList(row.id).then(res => {
|
||||
listQuery.value = {
|
||||
...res,
|
||||
nl: IdCard(res.yjRysfzh, 3) || "",
|
||||
xb: IdCard(res.yjRysfzh, 2) || "",
|
||||
xsd: res.xsd + '%'
|
||||
}
|
||||
})
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
listQuery.value = {};
|
||||
loading.value = false;
|
||||
dialogForm.value = false;
|
||||
listQuery.value = {}
|
||||
};
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/css/layout.scss";
|
||||
@import "~@/assets/css/element-plus.scss";
|
||||
::v-deep {
|
||||
.el-form-item__content {
|
||||
align-items: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="dialog" v-if="dialogForm">
|
||||
<div class="head_box">
|
||||
<span class="title">组合预警{{ title }} </span>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="small" :loading="loading" @click="submit">保存</el-button> -->
|
||||
<el-button size="small" @click="close">关闭</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form_cnt">
|
||||
<FormMessage :formList="formData" v-model="listQuery" ref="elform">
|
||||
</FormMessage>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormMessage from "@/components/aboutTable/FormMessage.vue";
|
||||
import { tbYjxxGetInfo, yjzxZhyjSelectList } from "@/api/yj.js";
|
||||
import { IdCard } from "@/utils/validate.js";
|
||||
import {
|
||||
ref,
|
||||
defineExpose,
|
||||
reactive,
|
||||
defineEmits,
|
||||
getCurrentInstance,
|
||||
watch
|
||||
} from "vue";
|
||||
const emit = defineEmits(["updateDate"]);
|
||||
const props = defineProps({
|
||||
dict: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const dialogForm = ref(false); //弹窗
|
||||
const formData = ref([]);
|
||||
watch(
|
||||
() => props.dict,
|
||||
(res) => {
|
||||
if (res) {
|
||||
formData.value = [
|
||||
{ label: "预警人姓名", prop: "xm", type: "input" },
|
||||
{ label: "身份证号", prop: "sfzh", type: "input" },
|
||||
{ label: "电话", prop: "dh", type: "input" },
|
||||
{ label: "组合大类", prop: "sfdlmc", type: "input" },
|
||||
{ label: "组合小类", prop: "sfzlmc", type: "input" },
|
||||
{ label: "组合预警次数", prop: "sfcs", type: "input", lx: "number" },
|
||||
{
|
||||
label: "标签颜色",
|
||||
prop: "bqys",
|
||||
type: "select",
|
||||
options: props.dict.D_GS_SSYJ
|
||||
},
|
||||
{ label: "预警时间", prop: "yjsj", type: "input" },
|
||||
{ label: "预警分值", prop: "sffz", type: "input", lx: "number" },
|
||||
{
|
||||
label: "处置状态",
|
||||
prop: "czzt",
|
||||
type: "select",
|
||||
options: props.dict.D_GSXT_YJXX_CZZT
|
||||
},
|
||||
{ label: "所属部门", prop: "ssbm", type: "input" },
|
||||
{ label: "所属县局", prop: "ssxgaj", type: "input" },
|
||||
{ label: "所属市局", prop: "sssgaj", type: "input" },
|
||||
{ label: "接警员姓名", prop: "jjyxm", type: "input" }
|
||||
// { label: "预警内容", prop: "yjNr", type: "textarea", width: "100%" },
|
||||
];
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
const listQuery = ref({}); //表单
|
||||
const loading = ref(false);
|
||||
const elform = ref();
|
||||
const title = ref("详情");
|
||||
const init = (row) => {
|
||||
dialogForm.value = true;
|
||||
yjzxZhyjSelectList(row.id).then((res) => {
|
||||
listQuery.value = {
|
||||
...res,
|
||||
nl: IdCard(res.yjRysfzh, 3) || "",
|
||||
xb: IdCard(res.yjRysfzh, 2) || "",
|
||||
xsd: res.xsd + "%"
|
||||
};
|
||||
});
|
||||
};
|
||||
// 关闭
|
||||
const close = () => {
|
||||
listQuery.value = {};
|
||||
loading.value = false;
|
||||
dialogForm.value = false;
|
||||
listQuery.value = {};
|
||||
};
|
||||
|
||||
defineExpose({ init });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/css/layout.scss";
|
||||
@import "~@/assets/css/element-plus.scss";
|
||||
::v-deep {
|
||||
.el-form-item__content {
|
||||
align-items: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,402 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 搜索 -->
|
||||
<div ref="searchBox" class="mt10 mb10">
|
||||
<Searchs :searchArr="searchConfiger" @submit="onSearch" @reset="reset" :key="pageData.keyCount">
|
||||
<template #jfd>
|
||||
<div>
|
||||
<el-input v-model="queryFrom.ksfz" type="number" placeholder="开始身份分值" style="width: 130px"></el-input>
|
||||
<span style="color: #333;margin: 0 4px;">至</span>
|
||||
<el-input v-model="queryFrom.jsfz" type="number" placeholder="结束身份分值" style="width: 130px"></el-input>
|
||||
</div>
|
||||
</template>
|
||||
</Searchs>
|
||||
</div>
|
||||
<PageTitle :malginLeft="10" :height="35" backgroundColor="#ffff" :marginBottom="5" :marginTop="5">
|
||||
<template #left>
|
||||
<el-button type="primary" size="small" @click="exportExl">导出</el-button>
|
||||
<el-button type="primary" size="small" @click="handleQs">签收</el-button>
|
||||
</template>
|
||||
</PageTitle>
|
||||
<!-- 表格 -->
|
||||
<div class="tabBox tabBox_zdy heightBox" :style="{ height: (pageData.tableHeight + 40) + 'px' }">
|
||||
<MyTable :tableData="pageData.tableData" :tableColumn="pageData.tableColumn" :tableHeight="pageData.tableHeight"
|
||||
:key="pageData.keyCount" :tableConfiger="pageData.tableConfiger" :controlsWidth="pageData.controlsWidth" expand
|
||||
@chooseData="handleChooseData">
|
||||
<template #expand="{ props }">
|
||||
<div style="max-width: 100%">
|
||||
<Items :row="props || {}" :dict="dict" />
|
||||
</div>
|
||||
</template>
|
||||
<template #sfcs="{ row }">
|
||||
<span style="color: #0072ff;" @click="handleClick(row)">{{ row.sfcs }}</span>
|
||||
</template>
|
||||
<template #czzt="{ row }">
|
||||
<DictTag :value="row.czzt" :options="D_GSXT_YJXX_CZZT" />
|
||||
</template>
|
||||
<template #sffz>
|
||||
<el-table-column prop="bqfz" width="80" align="center" label="标签分值" />
|
||||
<el-table-column prop="pzxs" width="60" align="center" label="系数" />
|
||||
<el-table-column prop="sffz" width="90" align="center" label="计算分值"/>
|
||||
</template>
|
||||
<template #controls="{ row }">
|
||||
<el-link type="warning" @click="pushAssess(row)">全息档案</el-link>
|
||||
<el-link type="primary" @click="handleCzjy(row)" v-if="roleCode">处置建议</el-link>
|
||||
<el-link type="primary" @click="chooseJfFun(row)">配置系统</el-link>
|
||||
<!-- <el-link type="primary" @click="showDetail(row)">转合成</el-link>
|
||||
<el-link type="danger" @click="handleQsFk(row)">转会商</el-link> -->
|
||||
<el-link type="success" @click="handleQsFk(row, '签收')" v-if="row.czzt == '01' && permission_sfqs">签收</el-link>
|
||||
<el-link type="success" @click="handleQsFk(row, '反馈')"
|
||||
v-else-if="row.czzt == '02' && permission_sfqs">反馈</el-link>
|
||||
<!-- <el-link type="success" @click="handleQsFk(row, '查看反馈')" v-else>查看反馈</el-link> -->
|
||||
<el-link type="primary" @click="openAddFrom(row)">详情</el-link>
|
||||
</template>
|
||||
</MyTable>
|
||||
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"></Pages>
|
||||
</div>
|
||||
</div>
|
||||
<Detail ref="detailRef" />
|
||||
<HolographicArchive v-model="assessShow" :dataList="dataList" />
|
||||
<FkDialog @change="getList" lx="03" />
|
||||
<Information v-model="showDialog" title="发送指令" @submit='submit' @close='closeFszl'>
|
||||
<SemdFqzl ref="semdFqzlRef" :itemData="itemData" @handleClose="handleClose" identification="yj"
|
||||
:tacitly="tacitly" />
|
||||
</Information>
|
||||
<AddFrom ref="addModelRef" :dict="{ D_GSXT_YJXX_CZZT, D_BZ_YJJB, D_GS_SSYJ }" />
|
||||
|
||||
<!-- 处置建议 -->
|
||||
<Czjy ref="czjyRef" @okSubmit="getList"></Czjy>
|
||||
|
||||
<ChooseJf v-model="chooseJfShow" titleValue="选择系数" :Single="false" :chooseJfBh="chooseJfBh" url="/yjzxSfyj/sjxspz"
|
||||
:roleIds="roleIds" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Czjy from './components/czjy.vue'
|
||||
import { getItem, setItem } from '@/utils/storage'
|
||||
import PageTitle from "@/components/aboutTable/PageTitle.vue";
|
||||
import Searchs from "@/components/aboutTable/Search.vue";
|
||||
import MyTable from "@/components/aboutTable/MyTable.vue";
|
||||
import Pages from "@/components/aboutTable/Pages.vue";
|
||||
import HolographicArchive from '@/views/home/components/holographicArchive.vue'
|
||||
import Information from "@/views/home/model/information.vue";
|
||||
import SemdFqzl from '@/components/instructionHasBeen/sendFqzl.vue'
|
||||
import AddFrom from "./components/addFrom.vue";
|
||||
import ChooseJf from '@/components/ChooseList/ChooseJf/index.vue'
|
||||
import FkDialog from "@/views/backOfficeSystem/fourColorManage/warningControl/centerHome/components/fkDialog.vue";
|
||||
import { reactive, ref, onMounted, getCurrentInstance, nextTick } from "vue";
|
||||
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
|
||||
import { yjzxSfyjSelectList, yjzxyjzxSfyjSelectList } from "@/api/yj.js";
|
||||
import { tbGsxtBqglSelectList } from '@/api/zdr'
|
||||
import Detail from './components/detail.vue'
|
||||
import { watch } from "vue";
|
||||
import emitter from "@/utils/eventBus.js";
|
||||
import { holographicProfileJump } from "@/utils/tools.js"
|
||||
import Items from "./item/items.vue"
|
||||
import { exportExlByObj } from "@/utils/exportExcel.js"
|
||||
import { getMultiDictVal } from "@/utils/dict.js"
|
||||
const czjyRef = ref()
|
||||
const { proxy } = getCurrentInstance();
|
||||
const searchBox = ref();
|
||||
const { D_GSXT_YJXX_CZZT, D_GS_SSYJ, D_BZ_YJJB } = proxy.$dict("D_GSXT_YJXX_CZZT", "D_GS_SSYJ", 'D_BZ_YJJB')
|
||||
const dict = reactive({ D_GSXT_YJXX_CZZT, D_GS_SSYJ })
|
||||
// 搜索配置
|
||||
const searchConfiger = ref([
|
||||
{ label: "姓名", prop: 'xm', placeholder: "请输入姓名", showType: "input" },
|
||||
{ label: "身份证号码", prop: 'sfzh', placeholder: "请输入身份证号码", showType: "input" },
|
||||
{ label: "预警标签", prop: 'yjbqmc', placeholder: "请输入预警标签", showType: "input" },
|
||||
{ label: "部门", prop: 'ssbmdm', placeholder: "请选择部门", showType: "department" },
|
||||
{ label: "级别", prop: 'bqys', placeholder: "请选择级别", showType: "select", options: D_BZ_YJJB },
|
||||
{ label: "积分段", prop: 'jfd', placeholder: "请选择积分段", showType: "Slot" },
|
||||
{ label: "预警时间", prop: 'times', showType: "datetimerange" },
|
||||
]);
|
||||
|
||||
const permission_sfqs = ref(false)
|
||||
const roleCode = ref(false)
|
||||
|
||||
const queryFrom = ref({});
|
||||
|
||||
// 页面数据
|
||||
const pageData = reactive({
|
||||
tableData: [],
|
||||
keyCount: 0,
|
||||
tableConfiger: {
|
||||
rowHieght: 61,
|
||||
showSelectType: "checkBox",
|
||||
loading: false,
|
||||
haveControls: true,
|
||||
},
|
||||
total: 0,
|
||||
pageConfiger: {
|
||||
pageSize: 20,
|
||||
pageCurrent: 1
|
||||
},
|
||||
controlsWidth: 300, //操作栏宽度
|
||||
tableColumn: [
|
||||
{ label: "状态", prop: "czzt", showSolt: true },
|
||||
{ label: "预警时间", prop: "yjsj" ,width: 180},
|
||||
{ label: "姓名", prop: "xm" },
|
||||
{ label: "身份证号", prop: "sfzh" ,width: 180},
|
||||
{ label: "标签", prop: "yjbqmc" },
|
||||
{ label: "接收单位", prop: "ssbm" },
|
||||
{ label: "活动频次", prop: "sfcs",width: 90 },
|
||||
{ label: "预警分值", prop: "sffz",showSolt: true },
|
||||
]
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
let str = getItem('deptId') ? getItem('deptId')[0].deptLevel : ''
|
||||
permission_sfqs.value = str.startsWith('2' || '3') ? false : true;
|
||||
let rols = getItem('roleList') ? getItem('roleList') : []
|
||||
let obj = rols.find(item => {
|
||||
return ['JS_666666', 'JS_777777', 'JS_888888'].includes(item.roleCode)
|
||||
})
|
||||
roleCode.value = obj ? true : false;
|
||||
|
||||
gettbGsxtBqglSelectList()
|
||||
tabHeightFn();
|
||||
getList();
|
||||
});
|
||||
|
||||
|
||||
const onSearch = (val) => {
|
||||
queryFrom.value = { ...queryFrom.value, ...val };
|
||||
queryFrom.value.startTime = val.times ? val.times[0] : ''
|
||||
queryFrom.value.endTime = val.times ? val.times[1] : ''
|
||||
pageData.pageConfiger.pageCurrent = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
delete queryFrom.value.ksfz
|
||||
delete queryFrom.value.jsfz
|
||||
}
|
||||
|
||||
const changeNo = (val) => {
|
||||
pageData.pageConfiger.pageCurrent = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const changeSize = (val) => {
|
||||
pageData.pageConfiger.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const getList = () => {
|
||||
pageData.tableConfiger.loading = true;
|
||||
// TODO: 替换为实际的身份预警API接口
|
||||
let params = {
|
||||
...queryFrom.value,
|
||||
pageCurrent: pageData.pageConfiger.pageCurrent,
|
||||
pageSize: pageData.pageConfiger.pageSize,
|
||||
}
|
||||
delete params.times;
|
||||
yjzxSfyjSelectList(params).then((res) => {
|
||||
pageData.tableData = Array.isArray(res?.records) ? res.records : [];
|
||||
pageData.tableData = pageData.tableData.map(item => {
|
||||
return {
|
||||
...item,
|
||||
bqys_cname: getMultiDictVal(item.bqys, D_GS_SSYJ),
|
||||
czzt_cname: getMultiDictVal(item.czzt, D_GSXT_YJXX_CZZT),
|
||||
}
|
||||
})
|
||||
pageData.total = res?.total || 0;
|
||||
pageData.tableConfiger.loading = false;
|
||||
}).catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
});
|
||||
};
|
||||
// 标签
|
||||
const bqLbData = ref({
|
||||
bqDl: [],
|
||||
bqXl: []
|
||||
})
|
||||
const gettbGsxtBqglSelectList = (val) => {
|
||||
const promes = {
|
||||
bqLx: '01',
|
||||
bqLb: val ? '02' : "01",
|
||||
bqDlId: val ? bqLbData.value.bqDl.find(item => item.value == val)?.id : ""
|
||||
}
|
||||
tbGsxtBqglSelectList(promes).then((res) => {
|
||||
if (val) {
|
||||
queryFrom.value.bqxl = ''
|
||||
bqLbData.value.bqXl = res.data ? res.data.map(item => {
|
||||
return {
|
||||
label: item.bqMc,
|
||||
value: item.bqDm,
|
||||
}
|
||||
}) : []
|
||||
} else {
|
||||
bqLbData.value.bqDl = res ? res.map(item => {
|
||||
return {
|
||||
label: item.bqMc,
|
||||
value: item.bqDm,
|
||||
id: item.id
|
||||
}
|
||||
}) : []
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => bqLbData.value.bqXl, (res) => {
|
||||
bqLbData.value.bqXl = res
|
||||
}, { deep: true })
|
||||
|
||||
// 查看详情
|
||||
const detailRef = ref()
|
||||
const handleClick = (row) => {
|
||||
detailRef.value.init(row)
|
||||
}
|
||||
// 处理签收
|
||||
const handleQsFk = (val, type) => {
|
||||
switch (type) {
|
||||
case '签收':
|
||||
proxy.$confirm("是否确定要签收?", "警告", { type: "warning" }).then(() => {
|
||||
qcckPost({ id: val.id }, "/mosty-gsxt//yjzxSfyj/yjqs").then(() => {
|
||||
val.czzt = '02'
|
||||
getList()
|
||||
proxy.$message({ type: "success", message: "签收成功" });
|
||||
});
|
||||
})
|
||||
break;
|
||||
case '反馈':
|
||||
case '查看反馈':
|
||||
emitter.emit("openFkDialog", { id: val.id, type });
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 全息档案
|
||||
const assessShow = ref(false)
|
||||
const dataList = ref()
|
||||
const pushAssess = (val) => {
|
||||
return holographicProfileJump(val.yjlb, val) // 全息档案跳转
|
||||
}
|
||||
|
||||
// 发送指令
|
||||
const showDialog = ref(false)
|
||||
const itemData = ref()
|
||||
const showDetail = (item) => {
|
||||
showDialog.value = true;
|
||||
itemData.value = item
|
||||
}
|
||||
const handleClose = () => {
|
||||
showDialog.value = false;
|
||||
}
|
||||
const semdFqzlRef = ref()
|
||||
const tacitly = {
|
||||
title: 'yjbt',
|
||||
instructionContent: 'yjnr'
|
||||
}
|
||||
const submit = () => {
|
||||
semdFqzlRef.value.getsendFqzl()
|
||||
}
|
||||
const closeFszl = () => {
|
||||
semdFqzlRef.value.close()
|
||||
}
|
||||
const bqYs = (val) => {
|
||||
if (val == '01') {
|
||||
return '#ff0202'
|
||||
} else if (val == '02') {
|
||||
return '#ff8c00'
|
||||
} else if (val == '03') {
|
||||
return '#ffff00'
|
||||
} else if (val == '04') {
|
||||
return '#0000ff'
|
||||
}
|
||||
}
|
||||
|
||||
// 新增
|
||||
const addModelRef = ref(null)
|
||||
const openAddFrom = (row) => {
|
||||
addModelRef.value.init('add', row)
|
||||
}
|
||||
// 选择系数
|
||||
const chooseJfShow = ref(false)
|
||||
const chooseJfBh = ref()
|
||||
const roleIds = ref()
|
||||
const chooseJfFun = (val) => {
|
||||
chooseJfBh.value = val.id
|
||||
yjzxyjzxSfyjSelectList(val.id).then(res => {
|
||||
roleIds.value = res.sjxspzList.map(item => item.xsid)
|
||||
chooseJfShow.value = true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const handleCzjy = (row) => {
|
||||
czjyRef.value.init(row)
|
||||
}
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
pageData.tableHeight = window.innerHeight - searchBox.value.offsetHeight - 280;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
/** 选中项 */
|
||||
const selectRows = ref([])
|
||||
const handleChooseData = (val) => {
|
||||
selectRows.value = val
|
||||
}
|
||||
const exportExl = () => {
|
||||
const titleObj = {
|
||||
czzt_cname: "状态",
|
||||
yjsj: "预警时间",
|
||||
xm: "姓名",
|
||||
sfzh: "身份证号",
|
||||
yjbqmc: "标签",
|
||||
ssbm: "接收单位",
|
||||
sfcs: "活动频次",
|
||||
sffz: "预警分值",
|
||||
}
|
||||
/** 导出【选中】的数据 (没有就全部)*/
|
||||
const needArr = selectRows.value?.length > 0 ? selectRows.value : pageData.tableData
|
||||
const data = needArr.map(item => {
|
||||
return {
|
||||
...item,
|
||||
bqys_cname: getMultiDictVal(item.bqys, D_GS_SSYJ),
|
||||
czzt_cname: getMultiDictVal(item.czzt, D_GSXT_YJXX_CZZT),
|
||||
}
|
||||
})
|
||||
exportExlByObj(titleObj, data, '身份预警')
|
||||
}
|
||||
|
||||
const handleQs = () => {
|
||||
if (selectRows.value?.length === 0) return proxy.$message({ type: "warning", message: "请选择要签收的预警" });
|
||||
let wqs = selectRows.value.filter(item => item.czzt == '01');
|
||||
if (wqs.length == 0) return proxy.$message({ type: "warning", message: "数据都已签收,请选择未签收的数据" });
|
||||
let yqs = selectRows.value.filter(item => item.czzt == '02');
|
||||
let texy = yqs.length > 0 ? `${yqs.length}条已签收预警数据,确认要签收${wqs.length}条未签收预警数据吗?` : '确认要签收所有预警数据吗?'
|
||||
proxy.$confirm(texy, "警告", { type: "warning" }).then(() => {
|
||||
let ids = wqs.map(item => item.id)
|
||||
qcckPost({ids}, '/mosty-gsxt/yjzxSfyj/batchQs').then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
}).catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
}).catch(() => { });
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style lang="scss">
|
||||
.el-loading-mask {
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
.tabBox_zdy {
|
||||
.el-table--fit {
|
||||
overflow: unset !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,564 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 搜索 -->
|
||||
<div ref="searchBox" class="mt10">
|
||||
<QueryFormPanel :fields="searchConfiger" @search="onSearch">
|
||||
<template #yjfz>
|
||||
<div>
|
||||
<el-input
|
||||
v-model="queryFrom.yjksfz"
|
||||
type="number"
|
||||
placeholder="开始分值"
|
||||
style="width: 130px"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-input
|
||||
v-model="queryFrom.yjjsfz"
|
||||
type="number"
|
||||
placeholder="结束分值"
|
||||
style="width: 130px"
|
||||
clearable
|
||||
></el-input>
|
||||
</div>
|
||||
</template>
|
||||
<template #but>
|
||||
<el-button type="primary" @click="exportExl" size="small"
|
||||
>导出</el-button
|
||||
>
|
||||
<el-button type="primary" size="small" @click="handleQs"
|
||||
>签收</el-button
|
||||
>
|
||||
</template>
|
||||
</QueryFormPanel>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<div :style="{ height: pageData.tableHeight + 40 + 'px' }" class="bgTable">
|
||||
<WarnDataTable
|
||||
:loading="pageData.tableConfiger.loading"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:data="pageData.tableData"
|
||||
:columns="pageData.tableColumn"
|
||||
table-class="warn-table"
|
||||
@selectionChange="handleChooseData"
|
||||
>
|
||||
<template #sfcs="{ row }">
|
||||
<span style="color: #0072ff" @click="handleClick(row)">{{
|
||||
row.sfcs
|
||||
}}</span>
|
||||
</template>
|
||||
<template #czzt="{ row }">
|
||||
<DictTag
|
||||
:value="row.czzt"
|
||||
:color="row.czzt === '01' ? '#ff2424' : '#1d72e8'"
|
||||
:tag="false"
|
||||
:options="D_GSXT_YJXX_CZZT"
|
||||
/>
|
||||
</template>
|
||||
<template #yjLylx="{ row }">
|
||||
<DictTag :value="row.yjLylx" :tag="false" :options="D_BZ_YJBQLX" />
|
||||
</template>
|
||||
<template #yjjb="{ row }">
|
||||
<div :style="{ 'background-color': ys(row) }">
|
||||
<DictTag
|
||||
:value="row.yjjb"
|
||||
color="#fff"
|
||||
:tag="false"
|
||||
:options="D_BZ_YJJB"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #nl="{ row }">
|
||||
<div>{{ IdCard(row.rysfzh, 3) }}</div>
|
||||
</template>
|
||||
<template #xb="{ row }">
|
||||
<div>{{ IdCard(row.rysfzh, 2) }}</div>
|
||||
</template>
|
||||
<template #operation="{ row }">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<span class="warning" @click="pushAssess(row)">全息档案</span>
|
||||
<span class="primary" @click="handleCzjy(row)" v-if="roleCode"
|
||||
>处置建议</span
|
||||
>
|
||||
<span class="primary" @click="chooseJfFun(row)">配置系统</span>
|
||||
<span
|
||||
class="success"
|
||||
@click="handleQsFk(row, '签收')"
|
||||
v-if="row.czzt == '01' && permission_sfqs"
|
||||
>签收</span
|
||||
>
|
||||
<span
|
||||
class="success"
|
||||
@click="handleQsFk(row, '反馈')"
|
||||
v-else-if="row.czzt == '02' && permission_sfqs"
|
||||
>反馈</span
|
||||
>
|
||||
<span class="primary" @click="openAddFrom(row)">详情</span>
|
||||
</div>
|
||||
</template>
|
||||
</WarnDataTable>
|
||||
<Pages
|
||||
@changeNo="changeNo"
|
||||
@changeSize="changeSize"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"
|
||||
></Pages>
|
||||
</div>
|
||||
</div>
|
||||
<Detail ref="detailRef" />
|
||||
<HolographicArchive v-model="assessShow" :dataList="dataList" />
|
||||
<FkDialog @change="getList" lx="03" />
|
||||
<Information
|
||||
v-model="showDialog"
|
||||
title="发送指令"
|
||||
@submit="submit"
|
||||
@close="closeFszl"
|
||||
>
|
||||
<SemdFqzl
|
||||
ref="semdFqzlRef"
|
||||
:itemData="itemData"
|
||||
@handleClose="handleClose"
|
||||
identification="yj"
|
||||
:tacitly="tacitly"
|
||||
/>
|
||||
</Information>
|
||||
<AddFrom
|
||||
ref="addModelRef"
|
||||
:dict="{ D_GSXT_YJXX_CZZT, D_BZ_YJJB, D_GS_SSYJ }"
|
||||
/>
|
||||
|
||||
<Xwbq ref="xwbqRef" :dict="{ D_GSXT_YJXX_CZZT, D_BZ_YJJB, D_GS_SSYJ }" />
|
||||
<Sfbq ref="sfbqRef" :dict="{ D_GSXT_YJXX_CZZT, D_BZ_YJJB, D_GS_SSYJ }" />
|
||||
<!-- 处置建议 -->
|
||||
<Czjy ref="czjyRef" @okSubmit="getList"></Czjy>
|
||||
|
||||
<ChooseJf
|
||||
v-model="chooseJfShow"
|
||||
titleValue="选择系数"
|
||||
:Single="false"
|
||||
:chooseJfBh="chooseJfBh"
|
||||
url="/yjzxSfyj/sjxspz"
|
||||
:roleIds="roleIds"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Czjy from "./components/czjy.vue";
|
||||
import { getItem } from "@/utils/storage";
|
||||
import Pages from "@/components/aboutTable/Pages.vue";
|
||||
import HolographicArchive from "@/views/home/components/holographicArchive.vue";
|
||||
import Information from "@/views/home/model/information.vue";
|
||||
import SemdFqzl from "@/components/instructionHasBeen/sendFqzl.vue";
|
||||
import AddFrom from "./components/zhbq.vue";
|
||||
import Xwbq from "./components/xwbq.vue";
|
||||
import Sfbq from "./components/sfbq.vue";
|
||||
import ChooseJf from "@/components/ChooseList/ChooseJf/index.vue";
|
||||
import FkDialog from "@/views/backOfficeSystem/fourColorManage/warningControl/centerHome/components/fkDialog.vue";
|
||||
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
|
||||
import { qcckPost, qcckGet } from "@/api/qcckApi.js";
|
||||
import { yjzxSfyjSelectList, yjzxyjzxSfyjSelectList } from "@/api/yj.js";
|
||||
import { tbGsxtBqglSelectList } from "@/api/zdr";
|
||||
import Detail from "./components/detail.vue";
|
||||
import { watch } from "vue";
|
||||
import emitter from "@/utils/eventBus.js";
|
||||
import { holographicProfileJump, bqYs } from "@/utils/tools.js";
|
||||
import { exportExlByObj } from "@/utils/exportExcel.js";
|
||||
import { getMultiDictVal } from "@/utils/dict.js";
|
||||
import WarnDataTable from "@/views/backOfficeSystem/ces/components/WarnDataTable.vue";
|
||||
import QueryFormPanel from "@/views/backOfficeSystem/ces/components/QueryFormPanel.vue";
|
||||
import { IdCard } from "@/utils/dict.js";
|
||||
const czjyRef = ref();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const searchBox = ref();
|
||||
const { D_GSXT_YJXX_CZZT, D_GS_SSYJ, D_BZ_YJJB, D_BZ_YJBQLX } = proxy.$dict(
|
||||
"D_GSXT_YJXX_CZZT",
|
||||
"D_GS_SSYJ",
|
||||
"D_BZ_YJJB",
|
||||
"D_BZ_YJBQLX"
|
||||
);
|
||||
const dict = reactive({ D_GSXT_YJXX_CZZT, D_GS_SSYJ });
|
||||
|
||||
// 搜索配置
|
||||
const searchConfiger = ref([
|
||||
{
|
||||
key: "czzt",
|
||||
label: "处置状态",
|
||||
type: "select",
|
||||
options: D_GSXT_YJXX_CZZT,
|
||||
placeholder: "请选择级别"
|
||||
},
|
||||
{
|
||||
key: "startTime",
|
||||
label: "预警时间",
|
||||
type: "datetimerange",
|
||||
placeholder: "请选择预警时间"
|
||||
},
|
||||
{ key: "ryxm", label: "姓名", type: "input", placeholder: "请输入姓名" },
|
||||
{
|
||||
key: "rysfzh",
|
||||
label: "身份证号码",
|
||||
type: "input",
|
||||
placeholder: "请输入身份证号码"
|
||||
},
|
||||
{
|
||||
key: "yjLylx",
|
||||
label: "标签类型",
|
||||
type: "select",
|
||||
options: D_BZ_YJBQLX,
|
||||
placeholder: "请选择预警标签"
|
||||
},
|
||||
{
|
||||
key: "yjjb",
|
||||
label: "标签级别",
|
||||
type: "select",
|
||||
options: D_BZ_YJJB,
|
||||
placeholder: "请选择预警标签"
|
||||
},
|
||||
{
|
||||
key: "ssbmdm",
|
||||
label: "接收部门",
|
||||
type: "department",
|
||||
placeholder: "请选择接收部门"
|
||||
},
|
||||
{
|
||||
key: "yjcs",
|
||||
label: "活动频次",
|
||||
type: "input",
|
||||
placeholder: "请输入活动频次"
|
||||
},
|
||||
{
|
||||
key: "yjfz",
|
||||
label: "标签分值",
|
||||
type: "slot",
|
||||
placeholder: "请输入标签分值"
|
||||
}
|
||||
]);
|
||||
|
||||
const permission_sfqs = ref(false);
|
||||
const roleCode = ref(false);
|
||||
|
||||
const queryFrom = ref({});
|
||||
|
||||
// 页面数据
|
||||
const pageData = reactive({
|
||||
tableData: [],
|
||||
keyCount: 0,
|
||||
tableConfiger: {
|
||||
rowHieght: 61,
|
||||
showSelectType: "checkBox",
|
||||
loading: false,
|
||||
haveControls: true
|
||||
},
|
||||
total: 0,
|
||||
pageConfiger: {
|
||||
pageSize: 20,
|
||||
pageCurrent: 1
|
||||
},
|
||||
controlsWidth: 300, //操作栏宽度
|
||||
tableColumn: [
|
||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||
{ label: "处置状态", align: "center", slotName: "czzt" },
|
||||
{ prop: "yjsj", label: "预警时间", align: "center", width: 200 },
|
||||
{ prop: "ryxm", label: "姓名", align: "center" },
|
||||
{ label: "性别", align: "center", slotName: "xb", align: "center" },
|
||||
{ prop: "rysfzh", label: "身份证号", align: "center", width: 200 },
|
||||
{ label: "年龄", align: "center", slotName: "nl", align: "center" },
|
||||
{ label: "标签类型", align: "center", slotName: "yjLylx" },
|
||||
{ label: "标签级别", align: "center", slotName: "yjjb" },
|
||||
{ prop: "ssbm", label: "接收单位", align: "center", width: 200 },
|
||||
{ prop: "yjcs", label: "活动频次", align: "center" },
|
||||
{ prop: "yjfz", label: "标签分值", align: "center" },
|
||||
{ label: "操作", width: 200, slotName: "operation" }
|
||||
]
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
let str = getItem("deptId") ? getItem("deptId")[0].deptLevel : "";
|
||||
permission_sfqs.value = str.startsWith("2" || "3") ? false : true;
|
||||
let rols = getItem("roleList") ? getItem("roleList") : [];
|
||||
let obj = rols.find((item) => {
|
||||
return ["JS_666666", "JS_777777", "JS_888888"].includes(item.roleCode);
|
||||
});
|
||||
roleCode.value = obj ? true : false;
|
||||
|
||||
gettbGsxtBqglSelectList();
|
||||
tabHeightFn();
|
||||
getList();
|
||||
});
|
||||
|
||||
const onSearch = (val) => {
|
||||
queryFrom.value = { ...queryFrom.value, ...val };
|
||||
if (val.startTime && Array.isArray(val.startTime)) {
|
||||
queryFrom.value.startTime = val.startTime[0];
|
||||
queryFrom.value.endTime = val.startTime[1];
|
||||
}
|
||||
pageData.pageConfiger.pageCurrent = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const changeNo = (val) => {
|
||||
pageData.pageConfiger.pageCurrent = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const changeSize = (val) => {
|
||||
pageData.pageConfiger.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
const getList = () => {
|
||||
pageData.tableConfiger.loading = true;
|
||||
let params = {
|
||||
...queryFrom.value,
|
||||
pageCurrent: pageData.pageConfiger.pageCurrent,
|
||||
pageSize: pageData.pageConfiger.pageSize
|
||||
};
|
||||
qcckPost(params, "/mosty-gsxt/tbYjxx/getPageBqyjList")
|
||||
.then((res) => {
|
||||
pageData.tableData = Array.isArray(res?.records) ? res.records : [];
|
||||
pageData.total = res?.total || 0;
|
||||
pageData.tableConfiger.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
});
|
||||
};
|
||||
// 标签
|
||||
const bqLbData = ref({
|
||||
bqDl: [],
|
||||
bqXl: []
|
||||
});
|
||||
const gettbGsxtBqglSelectList = (val) => {
|
||||
const promes = {
|
||||
bqLx: "01",
|
||||
bqLb: val ? "02" : "01",
|
||||
bqDlId: val ? bqLbData.value.bqDl.find((item) => item.value == val)?.id : ""
|
||||
};
|
||||
tbGsxtBqglSelectList(promes).then((res) => {
|
||||
if (val) {
|
||||
queryFrom.value.bqxl = "";
|
||||
bqLbData.value.bqXl = res.data
|
||||
? res.data.map((item) => {
|
||||
return {
|
||||
label: item.bqMc,
|
||||
value: item.bqDm
|
||||
};
|
||||
})
|
||||
: [];
|
||||
} else {
|
||||
bqLbData.value.bqDl = res
|
||||
? res.map((item) => {
|
||||
return {
|
||||
label: item.bqMc,
|
||||
value: item.bqDm,
|
||||
id: item.id
|
||||
};
|
||||
})
|
||||
: [];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => bqLbData.value.bqXl,
|
||||
(res) => {
|
||||
bqLbData.value.bqXl = res;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 查看详情
|
||||
const detailRef = ref();
|
||||
const handleClick = (row) => {
|
||||
detailRef.value.init(row);
|
||||
};
|
||||
// 处理签收
|
||||
const handleQsFk = (val, type) => {
|
||||
switch (type) {
|
||||
case "签收":
|
||||
proxy
|
||||
.$confirm("是否确定要签收?", "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
qcckPost({ id: val.id }, "/mosty-gsxt//yjzxSfyj/yjqs").then(() => {
|
||||
val.czzt = "02";
|
||||
getList();
|
||||
proxy.$message({ type: "success", message: "签收成功" });
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "反馈":
|
||||
case "查看反馈":
|
||||
emitter.emit("openFkDialog", { id: val.id, type });
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// 全息档案
|
||||
const assessShow = ref(false);
|
||||
const dataList = ref();
|
||||
const pushAssess = (val) => {
|
||||
return holographicProfileJump(val.yjlb, val); // 全息档案跳转
|
||||
};
|
||||
|
||||
// 发送指令
|
||||
const showDialog = ref(false);
|
||||
const itemData = ref();
|
||||
const showDetail = (item) => {
|
||||
showDialog.value = true;
|
||||
itemData.value = item;
|
||||
};
|
||||
const handleClose = () => {
|
||||
showDialog.value = false;
|
||||
};
|
||||
const semdFqzlRef = ref();
|
||||
const tacitly = {
|
||||
title: "yjbt",
|
||||
instructionContent: "yjnr"
|
||||
};
|
||||
const submit = () => {
|
||||
semdFqzlRef.value.getsendFqzl();
|
||||
};
|
||||
const closeFszl = () => {
|
||||
semdFqzlRef.value.close();
|
||||
};
|
||||
|
||||
// 新增
|
||||
const addModelRef = ref(null);
|
||||
const xwbqRef = ref(null);
|
||||
const sfbqRef = ref(null);
|
||||
// 21 身份预警 22 行为预警 23 组合预警
|
||||
const openAddFrom = (row) => {
|
||||
switch (row.yjLylx) {
|
||||
case "22":
|
||||
xwbqRef.value.init(row);
|
||||
break;
|
||||
case "21":
|
||||
sfbqRef.value.init("add", row);
|
||||
break;
|
||||
case "23":
|
||||
addModelRef.value.init(row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
// 选择系数
|
||||
const chooseJfShow = ref(false);
|
||||
const chooseJfBh = ref();
|
||||
const roleIds = ref();
|
||||
const chooseJfFun = (val) => {
|
||||
chooseJfBh.value = val.id;
|
||||
yjzxyjzxSfyjSelectList(val.id).then((res) => {
|
||||
roleIds.value = res.sjxspzList.map((item) => item.xsid);
|
||||
chooseJfShow.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
const handleCzjy = (row) => {
|
||||
czjyRef.value.init(row);
|
||||
};
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
pageData.tableHeight =
|
||||
window.innerHeight - searchBox.value.offsetHeight - 230;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
/** 选中项 */
|
||||
const selectRows = ref([]);
|
||||
const handleChooseData = (val) => {
|
||||
selectRows.value = val;
|
||||
};
|
||||
const exportExl = () => {
|
||||
const titleObj = {
|
||||
czzt_cname: "状态",
|
||||
yjsj: "预警时间",
|
||||
xm: "姓名",
|
||||
sfzh: "身份证号",
|
||||
yjbqmc: "标签",
|
||||
bqys_cname: "级别",
|
||||
ssbm: "接收单位",
|
||||
sfcs: "活动频次",
|
||||
bqfz: "标签分值",
|
||||
pzxs: "系数",
|
||||
sffz: "计算分值"
|
||||
};
|
||||
/** 导出【选中】的数据 (没有就全部)*/
|
||||
const needArr =
|
||||
selectRows.value?.length > 0 ? selectRows.value : pageData.tableData;
|
||||
const data = needArr.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
bqys_cname: getMultiDictVal(item.bqys, D_GS_SSYJ),
|
||||
czzt_cname: getMultiDictVal(item.czzt, D_GSXT_YJXX_CZZT)
|
||||
};
|
||||
});
|
||||
exportExlByObj(titleObj, data, "身份预警");
|
||||
};
|
||||
|
||||
const handleQs = () => {
|
||||
if (selectRows.value?.length === 0)
|
||||
return proxy.$message({ type: "warning", message: "请选择要签收的预警" });
|
||||
let wqs = selectRows.value.filter((item) => item.czzt == "01");
|
||||
if (wqs.length == 0)
|
||||
return proxy.$message({
|
||||
type: "warning",
|
||||
message: "数据都已签收,请选择未签收的数据"
|
||||
});
|
||||
let yqs = selectRows.value.filter((item) => item.czzt == "02");
|
||||
let texy =
|
||||
yqs.length > 0
|
||||
? `${yqs.length}条已签收预警数据,确认要签收${wqs.length}条未签收预警数据吗?`
|
||||
: "确认要签收所有预警数据吗?";
|
||||
proxy
|
||||
.$confirm(texy, "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
let ids = wqs.map((item) => item.id);
|
||||
qcckPost({ ids }, "/mosty-gsxt/yjzxSfyj/batchQs")
|
||||
.then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
const ys = (item) => {
|
||||
switch (item.yjjb) {
|
||||
case "01":
|
||||
return "red";
|
||||
case "02":
|
||||
return "orange";
|
||||
case "03":
|
||||
return "yellow";
|
||||
case "04":
|
||||
return "blue";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-loading-mask {
|
||||
background: rgba(0, 0, 0, 0.5) !important;
|
||||
}
|
||||
|
||||
::v-deep .el-table .cell {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::v-deep .el-table .el-table__cell {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.bgTable {
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="warning-item" >
|
||||
<el-divider content-position="left">预警内容</el-divider>
|
||||
<div class="item-row" style="border: none;"> {{ props.row.yjnr }} </div>
|
||||
<el-empty v-if="!props.row.yjnr" :image-size="0.5" description="暂无数据" />
|
||||
|
||||
<el-divider content-position="left">处置建议</el-divider>
|
||||
<div class="item-row" v-for="(it,idx) in list" :key="idx">
|
||||
<div class="info-item">
|
||||
<span class="text">预警人姓名:{{ it.jryXm }}</span>
|
||||
<span class="text">建议时间:{{ it.jysj }}</span>
|
||||
<span class="text">所属部门:{{ it.ssbm }}</span>
|
||||
</div>
|
||||
<div class="info-item">建议内容:<span>{{ it.jynr || '暂无' }}</span></div>
|
||||
</div>
|
||||
<el-empty v-if="list.length === 0" :image-size="0.5" description="暂无数据" />
|
||||
<el-divider content-position="left">反馈内容</el-divider>
|
||||
<div class="item-row" v-for="(it,idx) in Fklist" :key="idx">
|
||||
<div class="info-item">
|
||||
<span class="text">处置地址:{{ it.czdz }}</span>
|
||||
<span class="text">处置时间:{{ it.czsj }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="text">常控不尿检理由:<span>{{ it.ckbnjly || '暂无' }}</span></span>
|
||||
<span class="text">常控处置反馈补充信息:<span>{{ it.ckczbcxx || '暂无' }}</span></span>
|
||||
<span class="text">常控立线侦察评估:<span>{{ it.cklxzcpg || '暂无' }}</span></span>
|
||||
<span class="text">常控立线侦察评估依据:<span>{{ it.cklxzcpgyj || '暂无' }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty :image-size="0.5" v-if="Fklist.length === 0" description="暂无数据" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted ,defineProps} from 'vue'
|
||||
import { qcckPost,qcckGet } from "@/api/qcckApi.js";
|
||||
const props = defineProps({
|
||||
row: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
})
|
||||
const list = ref([])
|
||||
const Fklist = ref([])
|
||||
onMounted(() => {
|
||||
if(props.row.id){
|
||||
qcckPost({yjid: props.row.id},'/mosty-gsxt/yjxx/czjy/getPageList').then((res) => {
|
||||
list.value = res.records || []
|
||||
})
|
||||
qcckGet({},'/mosty-gsxt/yjzxSfyj/'+ props.row.id).then((res) => {
|
||||
Fklist.value = res.fkList || []
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.warning-item {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.item-row{
|
||||
border-bottom: 1px dashed #e8e8e8;
|
||||
line-height: 36px;
|
||||
padding-left: 2rem;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
&:nth-last-child(1){
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.info-item{
|
||||
line-height: 36px;
|
||||
width: 100%;
|
||||
.text{
|
||||
display: inline-block;
|
||||
width: 25%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-empty{
|
||||
--el-empty-padding: 0px;
|
||||
margin-bottom: 18px;
|
||||
--el-empty-description-margin-top: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -4,12 +4,25 @@
|
||||
<div ref="searchBox" class="mt10 mb10">
|
||||
<!-- <Search :searchArr="searchConfiger" @submit="onSearch" ref="searchDom" :key="pageData.keyCount">
|
||||
</Search> -->
|
||||
<QueryFormPanel v-model="queryFrom" :fields="searchConfiger" ref="searchDom" @search='onSearch'>
|
||||
<QueryFormPanel
|
||||
v-model="queryFrom"
|
||||
:fields="searchConfiger"
|
||||
ref="searchDom"
|
||||
@search="onSearch"
|
||||
>
|
||||
<template #but>
|
||||
<el-button type="primary" size="small" @click="exportExl">批量导出</el-button>
|
||||
<el-button type="primary" size="small" @click="handleQs">批量签收</el-button>
|
||||
<el-button type="primary" size="small" @click="handleQs">批量分析</el-button>
|
||||
<el-button type="primary" size="small" @click="countPeople">计算人数</el-button>
|
||||
<el-button type="primary" size="small" @click="exportExl"
|
||||
>批量导出</el-button
|
||||
>
|
||||
<el-button type="primary" size="small" @click="handleQs"
|
||||
>批量签收</el-button
|
||||
>
|
||||
<el-button type="primary" size="small" @click="handleQs"
|
||||
>批量分析</el-button
|
||||
>
|
||||
<el-button type="primary" size="small" @click="countPeople"
|
||||
>计算人数</el-button
|
||||
>
|
||||
</template>
|
||||
</QueryFormPanel>
|
||||
</div>
|
||||
@ -22,20 +35,34 @@
|
||||
</template>
|
||||
</PageTitle> -->
|
||||
<!-- 表格 -->
|
||||
<div style="background-color: #fff;">
|
||||
<WarnDataTable :loading="pageData.tableConfiger.loading" :tableHeight="pageData.tableHeight"
|
||||
:data="pageData.tableData" :columns="pageData.tableColumn" table-class="warn-table"
|
||||
@selectionChange="handleChooseData">
|
||||
<div style="background-color: #fff">
|
||||
<WarnDataTable
|
||||
:loading="pageData.tableConfiger.loading"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:data="pageData.tableData"
|
||||
:columns="pageData.tableColumn"
|
||||
table-class="warn-table"
|
||||
@selectionChange="handleChooseData"
|
||||
>
|
||||
<template #status="{ row }">
|
||||
<DictTag :value="row.czzt" :color="row.czzt === '01' ? '#ff2424' : '#1d72e8'" :tag="false"
|
||||
:options="D_GSXT_YJXX_CZZT" />
|
||||
<DictTag
|
||||
:value="row.czzt"
|
||||
:color="row.czzt === '01' ? '#ff2424' : '#1d72e8'"
|
||||
:tag="false"
|
||||
:options="D_GSXT_YJXX_CZZT"
|
||||
/>
|
||||
</template>
|
||||
<template #xbdm="{ row }">
|
||||
<DictTag :value="row.xbdm" :tag="false" :options="D_BZ_XB" />
|
||||
</template>
|
||||
<template #yjJb="{ row }">
|
||||
<div :style="{ 'background-color': bqYs(row.yjJb) }">
|
||||
<DictTag :value="row.yjJb" color="#fff" :tag="false" :options="D_BZ_YJJB" />
|
||||
<DictTag
|
||||
:value="row.yjJb"
|
||||
color="#fff"
|
||||
:tag="false"
|
||||
:options="D_BZ_YJJB"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #bqdl="{ row }">
|
||||
@ -47,69 +74,210 @@
|
||||
<template #cszt="{ row }">
|
||||
<DictTag :value="row.cszt" :tag="false" :options="D_GS_CSZT" />
|
||||
</template>
|
||||
<template #qbly="{ row }">
|
||||
<DictTag :value="row.qbly" :tag="false" :options="D_BZ_QBLY" />
|
||||
</template>
|
||||
<template #qblyjb="{ row }">
|
||||
<DictTag :value="row.qblyjb" :tag="false" :options="D_BZ_QBLYJB" />
|
||||
</template>
|
||||
<template #operation="{ row }">
|
||||
<div style="display: flex;justify-content: space-between;">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<span class="primary" @click="handleQsSingle(row)">签收</span>
|
||||
<span class="primary" @click="particularsOpen(row)">详情</span>
|
||||
<span class="warning" @click="pushWarning(row)">指派</span>
|
||||
<span class="warning" v-if="row.sfbc != '1'" @click="failWarning(row)">报错</span>
|
||||
<span
|
||||
class="warning"
|
||||
v-if="row.sfbc != '1'"
|
||||
@click="failWarning(row)"
|
||||
>报错</span
|
||||
>
|
||||
<span class="primary" @click="payAttention(row)">关注</span>
|
||||
</div>
|
||||
</template>
|
||||
</WarnDataTable>
|
||||
<Pages @changeNo="changeNo" @changeSize="changeSize" :tableHeight="pageData.tableHeight" :pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"></Pages>
|
||||
<Pages
|
||||
@changeNo="changeNo"
|
||||
@changeSize="changeSize"
|
||||
:tableHeight="pageData.tableHeight"
|
||||
:pageConfiger="{
|
||||
...pageData.pageConfiger,
|
||||
total: pageData.total
|
||||
}"
|
||||
></Pages>
|
||||
</div>
|
||||
</div>
|
||||
<ZpForm v-model="warningShow" :dataList="dataList" />
|
||||
<Particulars v-model="particularsShow" :dataList="dataPres"
|
||||
:dict="{ D_BZ_XB, D_BZ_YJJB, D_GS_QLZDRLX, D_GS_ZDR_RYJB, D_GS_ZDR_GJLB }" />
|
||||
<Particulars
|
||||
v-model="particularsShow"
|
||||
:dataList="dataPres"
|
||||
:dict="{ D_BZ_XB, D_BZ_YJJB, D_GS_QLZDRLX, D_GS_ZDR_RYJB, D_GS_ZDR_GJLB }"
|
||||
/>
|
||||
<peopleConut v-model="searchOpen" :dataConut="dataConut" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getMultiDictVal } from "@/utils/dict.js"
|
||||
import { exportExlByObj } from "@/utils/exportExcel.js"
|
||||
import { getMultiDictVal } from "@/utils/dict.js";
|
||||
import { exportExlByObj } from "@/utils/exportExcel.js";
|
||||
import ZpForm from "./zpForm.vue";
|
||||
import { bqYs } from '@/utils/tools.js'
|
||||
import { bqYs } from "@/utils/tools.js";
|
||||
import Particulars from "./particulars.vue";
|
||||
import Search from "@/components/aboutTable/Search.vue";
|
||||
import WarnDataTable from '@/views/backOfficeSystem/ces/components/WarnDataTable.vue'
|
||||
import WarnDataTable from "@/views/backOfficeSystem/ces/components/WarnDataTable.vue";
|
||||
import QueryFormPanel from "@/views/backOfficeSystem/ces/components/QueryFormPanel.vue";
|
||||
import PageTitle from "@/components/aboutTable/PageTitle.vue";
|
||||
// import MyTable from "@/components/aboutTable/MyTable.vue";
|
||||
import Pages from "@/components/aboutTable/Pages.vue";
|
||||
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
|
||||
import { reactive, ref, onMounted, getCurrentInstance, } from "vue";
|
||||
import { reactive, ref, onMounted, getCurrentInstance } from "vue";
|
||||
import peopleConut from "./peopleConut.vue";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { D_BZ_YJLY, D_GS_QLZDRLX, D_GS_ZDR_GJLB, D_BZ_YJJB, D_GS_CSZT, D_GS_QLZDRYXX, D_BZ_XB, D_GSXT_YJXX_CZZT, D_GS_ZDR_RYJB, D_BZ_SF } = proxy.$dict('D_BZ_YJLY', 'D_GS_QLZDRLX', "D_BZ_YJJB", "D_GS_QLZDRYXX", "D_BZ_XB", "D_GSXT_YJXX_CZZT", "D_GS_ZDR_RYJB", 'D_GS_ZDR_GJLB', 'D_GS_CSZT', "D_BZ_SF"); //获取字典数据
|
||||
const {
|
||||
D_BZ_YJLY,
|
||||
D_GS_QLZDRLX,
|
||||
D_GS_ZDR_GJLB,
|
||||
D_BZ_YJJB,
|
||||
D_GS_CSZT,
|
||||
D_GS_QLZDRYXX,
|
||||
D_BZ_XB,
|
||||
D_GSXT_YJXX_CZZT,
|
||||
D_GS_ZDR_RYJB,
|
||||
D_BZ_SF,
|
||||
D_BZ_QBLY,
|
||||
D_BZ_QBLYJB
|
||||
} = proxy.$dict(
|
||||
"D_BZ_YJLY",
|
||||
"D_GS_QLZDRLX",
|
||||
"D_BZ_YJJB",
|
||||
"D_GS_QLZDRYXX",
|
||||
"D_BZ_XB",
|
||||
"D_GSXT_YJXX_CZZT",
|
||||
"D_GS_ZDR_RYJB",
|
||||
"D_GS_ZDR_GJLB",
|
||||
"D_GS_CSZT",
|
||||
"D_BZ_SF",
|
||||
"D_BZ_QBLY",
|
||||
"D_BZ_QBLYJB"
|
||||
); //获取字典数据
|
||||
const searchBox = ref(); //搜索框
|
||||
const warningShow = ref(false);
|
||||
const dataList = ref([]);
|
||||
const searchConfiger = ref(
|
||||
[
|
||||
{ key: 'startTime', label: '预警时间', type: 'datetimerange', placeholder: '请选择预警时间' },
|
||||
{ key: 'yjJb', label: '预警级别', type: 'select', options: D_BZ_YJJB, multiple: true, placeholder: '请选择预警级别' },
|
||||
{ key: 'ssbmdm', label: '接收单位', type: 'department', placeholder: '请选择接收单位' },
|
||||
{ key: 'sfglyj', label: '关联预警', type: 'select', options: D_BZ_SF, placeholder: '请选择关联预警' },
|
||||
{ key: 'yjRyxm', label: '姓名', type: 'input', placeholder: '请输入姓名' },
|
||||
{ key: 'xbdm', label: '性别', type: 'select', options: D_BZ_XB, placeholder: '请选择性别' },
|
||||
{ key: 'cszt', label: '超时状态', type: 'select', options: D_GS_CSZT, placeholder: '请选择超时状态' },
|
||||
{ key: 'bqdl', label: '人员类别', type: 'select', options: D_GS_QLZDRLX, placeholder: '请选择人员类别' },
|
||||
{ key: 'sfgz', label: '重点关注', type: 'select', options: D_BZ_SF, placeholder: '请选择重点关注' },
|
||||
{ key: 'sfzp', label: '二次指派', type: 'select', options: D_BZ_SF, placeholder: '请选择二次指派' },
|
||||
{ key: 'yjRysfzh', label: '身份证号码', type: 'input', placeholder: '请输入身份证号码' },
|
||||
{ key: 'ksnl', label: '开始年龄', type: 'input', placeholder: '请输入开始年龄' },
|
||||
{ key: 'jsnl', label: '结束年龄', type: 'input', placeholder: '请输入结束年龄' },
|
||||
{ key: 'yjCs', label: '预警次数', type: 'input', placeholder: '请输入预警次数' },
|
||||
{ key: 'bqdl', label: '人员级别', type: 'select', options: D_GS_ZDR_RYJB },
|
||||
{ key: 'yjLylx', label: '轨迹类别', type: 'select', options: D_GS_ZDR_GJLB },
|
||||
{ key: 'yjDz', label: '活动发生地', type: 'input', placeholder: '请输入活动发生地' },
|
||||
{ key: 'yjbqmc', label: '人员细类', type: 'input', placeholder: '请输入人员细类' }
|
||||
])
|
||||
const searchConfiger = ref([
|
||||
{
|
||||
key: "startTime",
|
||||
label: "预警时间",
|
||||
type: "datetimerange",
|
||||
placeholder: "请选择预警时间"
|
||||
},
|
||||
{
|
||||
key: "yjJb",
|
||||
label: "预警级别",
|
||||
type: "select",
|
||||
options: D_BZ_YJJB,
|
||||
multiple: true,
|
||||
placeholder: "请选择预警级别"
|
||||
},
|
||||
{
|
||||
key: "ssbmdm",
|
||||
label: "接收单位",
|
||||
type: "department",
|
||||
placeholder: "请选择接收单位"
|
||||
},
|
||||
{
|
||||
key: "sfglyj",
|
||||
label: "关联预警",
|
||||
type: "select",
|
||||
options: D_BZ_SF,
|
||||
placeholder: "请选择关联预警"
|
||||
},
|
||||
{ key: "yjRyxm", label: "姓名", type: "input", placeholder: "请输入姓名" },
|
||||
{
|
||||
key: "xbdm",
|
||||
label: "性别",
|
||||
type: "select",
|
||||
options: D_BZ_XB,
|
||||
placeholder: "请选择性别"
|
||||
},
|
||||
{
|
||||
key: "cszt",
|
||||
label: "超时状态",
|
||||
type: "select",
|
||||
options: D_GS_CSZT,
|
||||
placeholder: "请选择超时状态"
|
||||
},
|
||||
{
|
||||
key: "bqdl",
|
||||
label: "人员类别",
|
||||
type: "select",
|
||||
options: D_GS_QLZDRLX,
|
||||
placeholder: "请选择人员类别"
|
||||
},
|
||||
{
|
||||
key: "sfgz",
|
||||
label: "重点关注",
|
||||
type: "select",
|
||||
options: D_BZ_SF,
|
||||
placeholder: "请选择重点关注"
|
||||
},
|
||||
{
|
||||
key: "sfzp",
|
||||
label: "二次指派",
|
||||
type: "select",
|
||||
options: D_BZ_SF,
|
||||
placeholder: "请选择二次指派"
|
||||
},
|
||||
{
|
||||
key: "yjRysfzh",
|
||||
label: "身份证号码",
|
||||
type: "input",
|
||||
placeholder: "请输入身份证号码"
|
||||
},
|
||||
{
|
||||
key: "ksnl",
|
||||
label: "开始年龄",
|
||||
type: "input",
|
||||
placeholder: "请输入开始年龄"
|
||||
},
|
||||
{
|
||||
key: "jsnl",
|
||||
label: "结束年龄",
|
||||
type: "input",
|
||||
placeholder: "请输入结束年龄"
|
||||
},
|
||||
{
|
||||
key: "yjCs",
|
||||
label: "预警次数",
|
||||
type: "input",
|
||||
placeholder: "请输入预警次数"
|
||||
},
|
||||
{ key: "bqdl", label: "人员级别", type: "select", options: D_GS_ZDR_RYJB },
|
||||
{ key: "yjLylx", label: "轨迹类别", type: "select", options: D_GS_ZDR_GJLB },
|
||||
{
|
||||
key: "yjDz",
|
||||
label: "活动发生地",
|
||||
type: "input",
|
||||
placeholder: "请输入活动发生地"
|
||||
},
|
||||
{
|
||||
key: "yjbqmc",
|
||||
label: "人员细类",
|
||||
type: "input",
|
||||
placeholder: "请输入人员细类"
|
||||
},
|
||||
{
|
||||
key: "qbly",
|
||||
label: "情报来源",
|
||||
type: "select",
|
||||
options: D_BZ_QBLY,
|
||||
placeholder: "请选择情报来源"
|
||||
},
|
||||
{
|
||||
key: "qblyjb",
|
||||
label: "情报来源级别",
|
||||
type: "select",
|
||||
options: D_BZ_QBLYJB,
|
||||
placeholder: "请选择情报来源级别"
|
||||
}
|
||||
]);
|
||||
|
||||
const queryFrom = ref({});
|
||||
const pageData = reactive({
|
||||
@ -129,91 +297,92 @@ const pageData = reactive({
|
||||
}, //分页
|
||||
controlsWidth: 200, //操作栏宽度
|
||||
tableColumn: [
|
||||
{ type: 'index', label: '序号', width: 55, align: 'center' },
|
||||
{ label: '预警状态', width: 86, align: 'center', slotName: 'status' },
|
||||
{ prop: 'yjSj', label: '预警时间', width: 150 },
|
||||
{ prop: 'yjRyxm', label: '人员姓名', width: 80 },
|
||||
{ prop: 'yjRysfzh', label: '身份证号', width: 158 },
|
||||
{ label: '性别', width: 56, align: 'center', slotName: 'xbdm' },
|
||||
{ prop: 'nl', label: '年龄', width: 56, align: 'center' },
|
||||
{ label: '预警级别', width: 88, align: 'center', slotName: 'yjJb' },
|
||||
{ label: '人员类别', width: 90, align: 'center', slotName: 'bqdl' },
|
||||
{ prop: 'yjbqmc', label: '人员细类', width: 92 },
|
||||
{ label: '轨迹类别', width: 92, align: 'center', slotName: 'yjLylx' },
|
||||
{ prop: 'yjDz', label: '活动发生地' },
|
||||
{ prop: 'ssbm', label: '接收单位' },
|
||||
{ prop: 'yjCs', label: '次数', width: 60, align: 'center' },
|
||||
{ label: '操作', width: 180, slotName: 'operation' },
|
||||
{ label: '超时状态', width: 80, align: 'center', slotName: 'cszt' }
|
||||
|
||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||
{ label: "预警状态", width: 80, align: "center", slotName: "status" },
|
||||
{ prop: "yjSj", label: "预警时间", width: 145 },
|
||||
{ prop: "yjRyxm", label: "人员姓名", width: 80 },
|
||||
{ prop: "yjRysfzh", label: "身份证号", width: 158 },
|
||||
{ label: "性别", width: 55, align: "center", slotName: "xbdm" },
|
||||
{ prop: "nl", label: "年龄", width: 55, align: "center" },
|
||||
{ label: "预警级别", width: 80, align: "center", slotName: "yjJb" },
|
||||
{ label: "人员类别", width: 80, align: "center", slotName: "bqdl" },
|
||||
{ prop: "yjbqmc", label: "人员细类", width: 80 },
|
||||
{ label: "轨迹类别", width: 80, align: "center", slotName: "yjLylx" },
|
||||
{ prop: "yjDz", label: "活动发生地", width: 100 },
|
||||
{ prop: "ssbm", label: "接收单位" },
|
||||
{ label: "情报来源", width: 80, align: "center", slotName: "qbly" },
|
||||
{ label: "来源级别", width: 80, align: "center", slotName: "qblyjb" },
|
||||
{ prop: "yjCs", label: "次数", width: 55, align: "center" },
|
||||
{ label: "操作", width: 180, slotName: "operation" },
|
||||
{ label: "超时状态", width: 80, align: "center", slotName: "cszt" }
|
||||
]
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
tabHeightFn();
|
||||
getList()
|
||||
getList();
|
||||
});
|
||||
|
||||
// 搜索
|
||||
const onSearch = (val) => {
|
||||
queryFrom.value = { ...val, yjJb: val.yjJb?.join(',') || '' }
|
||||
queryFrom.value.startTime = val.startTime ? val.startTime[0] : ''
|
||||
queryFrom.value.endTime = val.startTime ? val.startTime[1] : ''
|
||||
queryFrom.value = { ...val, yjJb: val.yjJb?.join(",") || "" };
|
||||
queryFrom.value.startTime = val.startTime ? val.startTime[0] : "";
|
||||
queryFrom.value.endTime = val.startTime ? val.startTime[1] : "";
|
||||
// queryFrom.value.sfglyj = val.sfglyj?.join(',') || ''
|
||||
// queryFrom.value.sfgz = val.sfgz?.join(',') || ''
|
||||
// queryFrom.value.sfzp = val.sfzp?.join(',') || ''
|
||||
pageData.pageConfiger.pageCurrent = 1;
|
||||
getList()
|
||||
}
|
||||
getList();
|
||||
};
|
||||
const changeNo = (val) => {
|
||||
pageData.pageConfiger.pageCurrent = val;
|
||||
getList()
|
||||
}
|
||||
getList();
|
||||
};
|
||||
const changeSize = (val) => {
|
||||
pageData.pageConfiger.pageSize = val;
|
||||
getList()
|
||||
}
|
||||
getList();
|
||||
};
|
||||
const getList = () => {
|
||||
pageData.tableConfiger.loading = true;
|
||||
const promes = {
|
||||
...queryFrom.value,
|
||||
pageCurrent: pageData.pageConfiger.pageCurrent,
|
||||
pageSize: pageData.pageConfiger.pageSize,
|
||||
|
||||
}
|
||||
pageSize: pageData.pageConfiger.pageSize
|
||||
};
|
||||
delete promes.times;
|
||||
qcckPost(promes, '/mosty-gsxt/tbYjxx/getQlzdrPageList').then((res) => {
|
||||
pageData.total = res.total || 0;
|
||||
pageData.tableConfiger.loading = false;
|
||||
pageData.tableData = res.records || []
|
||||
}).catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
})
|
||||
}
|
||||
qcckPost(promes, "/mosty-gsxt/tbYjxx/getQlzdrPageList")
|
||||
.then((res) => {
|
||||
pageData.total = res.total || 0;
|
||||
pageData.tableConfiger.loading = false;
|
||||
pageData.tableData = res.records || [];
|
||||
})
|
||||
.catch(() => {
|
||||
pageData.tableConfiger.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const pushWarning = (val) => {
|
||||
warningShow.value = true;
|
||||
dataList.value = val;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const failWarning = (val) => {
|
||||
let ids = [val.id]
|
||||
qcckPost({ ids }, '/mosty-gsxt/tbYjxx/yjbc').then((res) => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
}).catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let ids = [val.id];
|
||||
qcckPost({ ids }, "/mosty-gsxt/tbYjxx/yjbc")
|
||||
.then((res) => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
};
|
||||
|
||||
/** 选中项 */
|
||||
const selectRows = ref([])
|
||||
const selectRows = ref([]);
|
||||
const handleChooseData = (val) => {
|
||||
selectRows.value = val
|
||||
}
|
||||
selectRows.value = val;
|
||||
};
|
||||
const exportExl = () => {
|
||||
const titleObj = {
|
||||
czzt_name: "状态",
|
||||
@ -225,102 +394,138 @@ const exportExl = () => {
|
||||
yjbqmc: "细类",
|
||||
yjDz: "活动发生地",
|
||||
ssbm: "接收单位",
|
||||
yjCs: "预警次数",
|
||||
}
|
||||
yjCs: "预警次数"
|
||||
};
|
||||
/** 导出【选中】的数据 (没有就全部)*/
|
||||
const needArr = selectRows.value?.length > 0 ? selectRows.value : pageData.tableData
|
||||
const data = needArr.map(item => {
|
||||
const needArr =
|
||||
selectRows.value?.length > 0 ? selectRows.value : pageData.tableData;
|
||||
const data = needArr.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
czzt_name: getMultiDictVal(item.czzt, D_GSXT_YJXX_CZZT),
|
||||
yjJb_name: getMultiDictVal(item.yjJb, D_BZ_YJJB),
|
||||
bqdl_name: getMultiDictVal(item.bqdl, D_GS_QLZDRLX),
|
||||
}
|
||||
})
|
||||
exportExlByObj(titleObj, data, '七类重点')
|
||||
}
|
||||
bqdl_name: getMultiDictVal(item.bqdl, D_GS_QLZDRLX)
|
||||
};
|
||||
});
|
||||
exportExlByObj(titleObj, data, "七类重点");
|
||||
};
|
||||
// 批量签收
|
||||
const handleQs = () => {
|
||||
if (selectRows.value?.length === 0) return proxy.$message({ type: "warning", message: "请选择要签收的预警" });
|
||||
let wqs = selectRows.value.filter(item => item.czzt == '01');
|
||||
if (wqs.length == 0) return proxy.$message({ type: "warning", message: "数据都已签收,请选择未签收的数据" });
|
||||
let yqs = selectRows.value.filter(item => item.czzt == '02');
|
||||
let texy = yqs.length > 0 ? `${yqs.length}条已签收预警数据,确认要签收${wqs.length}条未签收预警数据吗?` : '确认要签收所有预警数据吗?'
|
||||
proxy.$confirm(texy, "警告", { type: "warning" }).then(() => {
|
||||
let ids = wqs.map(item => item.id)
|
||||
qcckPost({ ids }, '/mosty-gsxt/tbYjxx/batchQs').then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
}).catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
if (selectRows.value?.length === 0)
|
||||
return proxy.$message({ type: "warning", message: "请选择要签收的预警" });
|
||||
let wqs = selectRows.value.filter((item) => item.czzt == "01");
|
||||
if (wqs.length == 0)
|
||||
return proxy.$message({
|
||||
type: "warning",
|
||||
message: "数据都已签收,请选择未签收的数据"
|
||||
});
|
||||
}).catch(() => { });
|
||||
}
|
||||
let yqs = selectRows.value.filter((item) => item.czzt == "02");
|
||||
let texy =
|
||||
yqs.length > 0
|
||||
? `${yqs.length}条已签收预警数据,确认要签收${wqs.length}条未签收预警数据吗?`
|
||||
: "确认要签收所有预警数据吗?";
|
||||
proxy
|
||||
.$confirm(texy, "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
let ids = wqs.map((item) => item.id);
|
||||
qcckPost({ ids }, "/mosty-gsxt/tbYjxx/batchQs")
|
||||
.then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 详情
|
||||
const dataPres = ref({})
|
||||
const dataPres = ref({});
|
||||
const particularsShow = ref(false);
|
||||
const particularsOpen = (row) => {
|
||||
dataPres.value = row
|
||||
dataPres.value = row;
|
||||
|
||||
particularsShow.value = true;
|
||||
}
|
||||
};
|
||||
// 单条签收
|
||||
const handleQsSingle = (row) => {
|
||||
if (row.czzt == '02') return proxy.$message({ type: "warning", message: "数据已签收,无需重复签收" });
|
||||
proxy.$confirm('确认要签收该条预警数据吗?', "警告", { type: "warning" }).then(() => {
|
||||
qcckPost({ ids: [row.id] }, '/mosty-gsxt/tbYjxx/batchQs').then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
}).catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
if (row.czzt == "02")
|
||||
return proxy.$message({
|
||||
type: "warning",
|
||||
message: "数据已签收,无需重复签收"
|
||||
});
|
||||
}).catch(() => { });
|
||||
}
|
||||
proxy
|
||||
.$confirm("确认要签收该条预警数据吗?", "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
qcckPost({ ids: [row.id] }, "/mosty-gsxt/tbYjxx/batchQs")
|
||||
.then(() => {
|
||||
proxy.$message({ type: "success", message: "成功" });
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
proxy.$message({ type: "error", message: "失败" });
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 关注
|
||||
const payAttention = (row) => {
|
||||
let promes = {}
|
||||
if (row.sfgz == '1') {
|
||||
promes.sfgz = '0'
|
||||
promes.msg = '取消关注'
|
||||
let promes = {};
|
||||
if (row.sfgz == "1") {
|
||||
promes.sfgz = "0";
|
||||
promes.msg = "取消关注";
|
||||
} else {
|
||||
promes.sfgz = '1'
|
||||
promes.msg = '关注'
|
||||
promes.sfgz = "1";
|
||||
promes.msg = "关注";
|
||||
}
|
||||
|
||||
proxy.$confirm('确认要关注该条预警数据吗?', "警告", { type: "warning" }).then(() => {
|
||||
qcckPost({ sfgz: promes.sfgz, id: row.id }, '/mosty-gsxt/tbYjxx/yjgz').then(() => {
|
||||
proxy.$message({ type: "success", message: `${promes.msg}成功` });
|
||||
getList();
|
||||
}).catch(() => {
|
||||
proxy.$message({ type: "error", message: `${promes.msg}失败` });
|
||||
});
|
||||
}).catch(() => { });
|
||||
}
|
||||
proxy
|
||||
.$confirm("确认要关注该条预警数据吗?", "警告", { type: "warning" })
|
||||
.then(() => {
|
||||
qcckPost({ sfgz: promes.sfgz, id: row.id }, "/mosty-gsxt/tbYjxx/yjgz")
|
||||
.then(() => {
|
||||
proxy.$message({ type: "success", message: `${promes.msg}成功` });
|
||||
getList();
|
||||
})
|
||||
.catch(() => {
|
||||
proxy.$message({ type: "error", message: `${promes.msg}失败` });
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
// 人数计算
|
||||
const searchDom = ref(null)
|
||||
const dataConut = ref(0)
|
||||
const searchOpen = ref(false)
|
||||
const searchDom = ref(null);
|
||||
const dataConut = ref(0);
|
||||
const searchOpen = ref(false);
|
||||
const countPeople = () => {
|
||||
|
||||
const promes = {
|
||||
...searchDom.value.formState,
|
||||
yjJb: searchDom.value.formState.yjJb?.join(',') || '',
|
||||
startTime: searchDom.value.formState.startTime ? searchDom.value.formState.startTime[0] : '',
|
||||
endTime: searchDom.value.formState.endTime ? searchDom.value.formState.endTime[1] : '',
|
||||
}
|
||||
qcckPost(promes, '/mosty-gsxt/tbYjxx/bkyjQctj').then((res) => {
|
||||
dataConut.value = res || 0
|
||||
searchOpen.value = true
|
||||
}).catch(() => {
|
||||
// proxy.$message({ type: "error", message: `${promes.msg}失败` });
|
||||
});
|
||||
}
|
||||
yjJb: searchDom.value.formState.yjJb?.join(",") || "",
|
||||
startTime: searchDom.value.formState.startTime
|
||||
? searchDom.value.formState.startTime[0]
|
||||
: "",
|
||||
endTime: searchDom.value.formState.endTime
|
||||
? searchDom.value.formState.endTime[1]
|
||||
: ""
|
||||
};
|
||||
qcckPost(promes, "/mosty-gsxt/tbYjxx/bkyjQctj")
|
||||
.then((res) => {
|
||||
dataConut.value = res || 0;
|
||||
searchOpen.value = true;
|
||||
})
|
||||
.catch(() => {
|
||||
// proxy.$message({ type: "error", message: `${promes.msg}失败` });
|
||||
});
|
||||
};
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
console.log("xxxxxxx");
|
||||
|
||||
pageData.tableHeight = window.innerHeight - searchBox.value.offsetHeight - 230;
|
||||
window.onresize = function () { tabHeightFn(); };
|
||||
pageData.tableHeight =
|
||||
window.innerHeight - searchBox.value.offsetHeight - 230;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="luntan-tech-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>• 支持 JPG、PNG 格式</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>
|
||||
@import '../styles/luntan-tech.scss';
|
||||
|
||||
.avatar-upload-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px 0 8px;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2px solid rgba(0, 227, 255, 0.4);
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(10, 30, 60, 0.6);
|
||||
box-shadow: 0 0 20px rgba(0, 120, 200, 0.2);
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.preview-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: $lt-text-muted;
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 8px;
|
||||
color: rgba(0, 227, 255, 0.45);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
margin-bottom: 16px;
|
||||
|
||||
:deep(.el-button--primary) {
|
||||
background: linear-gradient(180deg, #00a3ff 0%, #0066bb 100%);
|
||||
border-color: rgba(0, 227, 255, 0.5);
|
||||
box-shadow: 0 0 12px rgba(0, 163, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.upload-tips {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
background: rgba(10, 30, 60, 0.65);
|
||||
border: 1px solid $lt-border-dim;
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/luntan-dialog-tech.scss';
|
||||
</style>
|
||||
@ -0,0 +1,534 @@
|
||||
<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">
|
||||
@import '../styles/luntan-tech.scss';
|
||||
|
||||
.comment-list {
|
||||
.comment-item {
|
||||
margin-bottom: 18px;
|
||||
padding-bottom: 18px;
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comment-main {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.comment-avatar {
|
||||
flex-shrink: 0;
|
||||
border: 1px solid rgba(0, 227, 255, 0.3);
|
||||
box-shadow: 0 0 8px rgba(0, 120, 180, 0.2);
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $lt-text;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.65;
|
||||
color: $lt-text-dim;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.comment-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.comment-time {
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.comment-actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: $lt-cyan;
|
||||
text-shadow: 0 0 8px rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $lt-cyan;
|
||||
}
|
||||
|
||||
&.toggle-btn {
|
||||
.el-icon {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.rotate-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-input-box {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
@include lt-panel-soft-bg;
|
||||
}
|
||||
|
||||
.reply-label {
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.reply-textarea {
|
||||
margin-bottom: 8px;
|
||||
|
||||
:deep(.el-textarea__inner) {
|
||||
background: rgba(10, 28, 58, 0.88) !important;
|
||||
color: $lt-text-dim;
|
||||
border: 1px solid rgba(0, 227, 255, 0.28);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 12px rgba(0, 80, 140, 0.15) inset;
|
||||
|
||||
&::placeholder {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.reply-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.el-button) {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
:deep(.el-button--default) {
|
||||
background: rgba(0, 40, 70, 0.5);
|
||||
border-color: rgba(0, 163, 255, 0.35);
|
||||
color: $lt-text-dim;
|
||||
}
|
||||
|
||||
:deep(.el-button--primary) {
|
||||
background: linear-gradient(180deg, #00a3ff 0%, #0066bb 100%);
|
||||
border-color: rgba(0, 227, 255, 0.45);
|
||||
box-shadow: 0 0 12px rgba(0, 163, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.replies-list {
|
||||
margin-top: 14px;
|
||||
padding-left: 12px;
|
||||
border-left: 2px solid rgba(0, 227, 255, 0.25);
|
||||
box-shadow: -2px 0 12px rgba(0, 100, 160, 0.08);
|
||||
}
|
||||
|
||||
.reply-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
align-items: flex-start;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-avatar {
|
||||
flex-shrink: 0;
|
||||
border: 1px solid rgba(0, 227, 255, 0.22);
|
||||
}
|
||||
|
||||
.reply-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.reply-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.reply-text {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: $lt-text-dim;
|
||||
margin-bottom: 6px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.reply-to {
|
||||
color: $lt-cyan;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.reply-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.reply-time {
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.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: 2px;
|
||||
background: rgba(0, 163, 255, 0.22);
|
||||
color: #b8ecff;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
border: 1px solid rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/luntan-v3emoji-tech.scss';
|
||||
</style>
|
||||
565
src/views/backOfficeSystem/luntan copy/components/PostDetail.vue
Normal file
565
src/views/backOfficeSystem/luntan copy/components/PostDetail.vue
Normal file
@ -0,0 +1,565 @@
|
||||
<template>
|
||||
<div class="post-detail luntan-tech-detail">
|
||||
<!-- 头部 -->
|
||||
<div class="detail-header">
|
||||
<el-button class="detail-back-btn" @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" class="author-avatar">
|
||||
<img src="@/assets/images/mr.png" />
|
||||
</el-avatar>
|
||||
<div class="author-info">
|
||||
<div class="author-name-row">
|
||||
<span class="author-name">{{ postData.userName }}</span>
|
||||
<span v-if="postData.userTag" class="level-badge">{{
|
||||
postData.userTag
|
||||
}}</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
|
||||
:preview-teleported="true"
|
||||
: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);
|
||||
}
|
||||
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">
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.luntan-tech-detail {
|
||||
background: transparent;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
padding: 14px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 163, 255, 0.08) 0%,
|
||||
transparent 55%
|
||||
);
|
||||
|
||||
:deep(.detail-back-btn.el-button) {
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 4px;
|
||||
background: rgba(10, 30, 60, 0.75) !important;
|
||||
border: 1px solid $lt-border-dim !important;
|
||||
color: $lt-text-dim !important;
|
||||
box-shadow: inset 0 1px 0 rgba(0, 220, 255, 0.08),
|
||||
0 0 12px rgba(0, 80, 140, 0.2);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $lt-cyan !important;
|
||||
border-color: rgba(0, 227, 255, 0.45) !important;
|
||||
background: rgba(12, 40, 75, 0.92) !important;
|
||||
box-shadow: 0 0 16px rgba(0, 163, 255, 0.28),
|
||||
inset 0 1px 0 rgba(0, 220, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.detail-back-btn .el-icon) {
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: $lt-text;
|
||||
margin-right: 60px;
|
||||
letter-spacing: 0.08em;
|
||||
text-shadow: 0 0 16px rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.post-main {
|
||||
padding: 20px 18px 18px;
|
||||
margin: 12px 12px 0;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
@include lt-panel-soft-bg;
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
padding: 8px 12px 32px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.premium-badge {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 227, 255, 0.35) 0%,
|
||||
rgba(0, 100, 180, 0.5) 100%
|
||||
);
|
||||
color: #fff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(0, 227, 255, 0.55);
|
||||
box-shadow: 0 0 14px rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
.post-author {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.author-avatar {
|
||||
border: 2px solid rgba(0, 227, 255, 0.35);
|
||||
box-shadow: 0 0 12px rgba(0, 163, 255, 0.25);
|
||||
}
|
||||
|
||||
.author-info {
|
||||
flex: 1;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.author-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 227, 255, 0.15);
|
||||
color: $lt-cyan;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
.author-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 163, 255, 0.25);
|
||||
color: #b8ecff;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
border: 1px solid rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
.publish-time {
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.post-content-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.85;
|
||||
color: $lt-text-dim;
|
||||
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: 4px;
|
||||
overflow: hidden;
|
||||
border: 1px solid $lt-border-dim;
|
||||
box-shadow: 0 0 12px rgba(0, 100, 160, 0.2);
|
||||
|
||||
:deep(.el-image) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.image-error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(10, 30, 60, 0.6);
|
||||
color: $lt-text-muted;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.post-stats {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: $lt-text-muted;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: $lt-cyan;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 14px;
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 12px 14px;
|
||||
font-size: 14px;
|
||||
color: $lt-text-muted;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&.active {
|
||||
color: $lt-cyan;
|
||||
font-weight: 600;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, $lt-cyan, transparent);
|
||||
box-shadow: 0 0 10px rgba(0, 227, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sort-buttons {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.top-input {
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
cursor: pointer;
|
||||
background: rgba(10, 30, 60, 0.65) !important;
|
||||
border-radius: 4px !important;
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.28) inset,
|
||||
0 0 16px rgba(0, 100, 180, 0.15) !important;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.45) inset,
|
||||
0 0 20px rgba(0, 163, 255, 0.25) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
color: $lt-text-dim;
|
||||
|
||||
&::placeholder {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
332
src/views/backOfficeSystem/luntan copy/components/PostItem.vue
Normal file
332
src/views/backOfficeSystem/luntan copy/components/PostItem.vue
Normal file
@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<div class="post-item" @click="handleClick">
|
||||
<div class="premium-badge" v-if="post.isPremium">置顶</div>
|
||||
<div class="post-main-content">
|
||||
<el-avatar :size="44" :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>
|
||||
<span v-if="post.userTag" class="level-badge">{{
|
||||
post.userTag
|
||||
}}</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" v-if="postTitle || bodySource">
|
||||
<div v-if="postTitle" class="post-title">{{ postTitle }}</div>
|
||||
<div v-if="bodySource" class="post-text-wrap">
|
||||
<div class="post-text">{{ displayBody }}</div>
|
||||
<span
|
||||
v-if="showFullLink"
|
||||
class="full-text-link"
|
||||
@click.stop="handleClick"
|
||||
>全文</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 图片展示 -->
|
||||
<div
|
||||
class="post-images"
|
||||
v-if="post.images && post.images.length > 0"
|
||||
:class="{ 'is-three': post.images.length >= 3 }"
|
||||
>
|
||||
<div
|
||||
v-for="(img, index) in post.images"
|
||||
:key="index"
|
||||
class="image-item"
|
||||
@click.stop
|
||||
>
|
||||
<el-image
|
||||
:preview-teleported="true"
|
||||
: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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { ChatDotRound, Picture } from "@element-plus/icons-vue";
|
||||
|
||||
const props = defineProps({
|
||||
post: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["like", "click"]);
|
||||
|
||||
const EXCERPT_LEN = 160;
|
||||
|
||||
const rawContent = computed(() => (props.post.content || "").trim());
|
||||
|
||||
const postTitle = computed(() => {
|
||||
const t = rawContent.value;
|
||||
if (!t) return "";
|
||||
const idx = t.indexOf("\n");
|
||||
if (idx === -1) return "";
|
||||
const first = t.slice(0, idx).trim();
|
||||
return first.length > 0 ? first : "";
|
||||
});
|
||||
|
||||
const bodySource = computed(() => {
|
||||
const t = rawContent.value;
|
||||
if (!t) return "";
|
||||
const idx = t.indexOf("\n");
|
||||
if (idx === -1) return t;
|
||||
const rest = t.slice(idx + 1).trim();
|
||||
return rest;
|
||||
});
|
||||
|
||||
const showFullLink = computed(() => bodySource.value.length > EXCERPT_LEN);
|
||||
|
||||
const displayBody = computed(() => {
|
||||
const b = bodySource.value;
|
||||
if (!b) return "";
|
||||
if (b.length <= EXCERPT_LEN) return b;
|
||||
return `${b.slice(0, EXCERPT_LEN)}…`;
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
emit("click", props.post);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.post-item {
|
||||
position: relative;
|
||||
padding: 18px 18px 14px;
|
||||
margin-bottom: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
@include lt-panel-frame;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(0, 227, 255, 0.55);
|
||||
box-shadow: $lt-glow-strong, inset 0 1px 0 rgba(0, 220, 255, 0.18);
|
||||
}
|
||||
}
|
||||
|
||||
.premium-badge {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 227, 255, 0.35) 0%,
|
||||
rgba(0, 100, 180, 0.45) 100%
|
||||
);
|
||||
color: #fff;
|
||||
padding: 3px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
border: 1px solid rgba(0, 227, 255, 0.55);
|
||||
box-shadow: 0 0 12px rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
.post-main-content {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.post-avatar {
|
||||
flex-shrink: 0;
|
||||
border: 2px solid rgba(0, 227, 255, 0.35);
|
||||
box-shadow: 0 0 10px rgba(0, 163, 255, 0.2);
|
||||
}
|
||||
|
||||
.post-right {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.post-header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.user-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 227, 255, 0.12);
|
||||
color: $lt-cyan;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
border: 1px solid rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
.post-time {
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
line-height: 1.45;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.post-text-wrap {
|
||||
font-size: 14px;
|
||||
line-height: 1.65;
|
||||
color: $lt-text-dim;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.full-text-link {
|
||||
margin-left: 4px;
|
||||
color: $lt-cyan;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-shadow: 0 0 8px rgba(0, 227, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.post-images {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 12px;
|
||||
|
||||
&.is-three .image-item {
|
||||
width: calc((100% - 16px) / 3);
|
||||
min-width: 0;
|
||||
aspect-ratio: 4 / 3;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.image-item {
|
||||
width: 160px;
|
||||
height: 112px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid $lt-border-dim;
|
||||
box-shadow: 0 0 10px rgba(0, 80, 140, 0.25);
|
||||
|
||||
:deep(.el-image) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.post-images.is-three .image-item {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.image-error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(10, 30, 60, 0.65);
|
||||
color: $lt-text-muted;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.action-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: $lt-text-muted;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: $lt-cyan;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.author-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 200, 180, 0.18);
|
||||
color: #9ff;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
</style>
|
||||
463
src/views/backOfficeSystem/luntan copy/components/PostList.vue
Normal file
463
src/views/backOfficeSystem/luntan copy/components/PostList.vue
Normal file
@ -0,0 +1,463 @@
|
||||
<template>
|
||||
<div class="post-list">
|
||||
<!-- 贴吧风格顶栏:左信息 + 右操作(页面背景仍为外层网格,不变) -->
|
||||
<div class="bar-head">
|
||||
<div class="bar-head-left">
|
||||
<div class="bar-logo">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
</div>
|
||||
<div class="bar-head-text">
|
||||
<div class="bar-title">信息论坛</div>
|
||||
<div class="bar-meta">帖子 {{ totalDisplay }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bar-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="bar-pill bar-pill-primary"
|
||||
@click="showPublishDialog = true"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
发帖
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 帖子列表 -->
|
||||
<div
|
||||
class="posts-container"
|
||||
v-loading="loading"
|
||||
v-infinite-scroll="loadMore"
|
||||
:infinite-scroll-disabled="scrollDisabled"
|
||||
:infinite-scroll-distance="100"
|
||||
>
|
||||
<!-- 置顶:仅标题缩略列表 -->
|
||||
<div v-if="pinnedList.length" class="pinned-block">
|
||||
<div class="pinned-list">
|
||||
<div
|
||||
v-for="post in pinnedList"
|
||||
:key="'zd-' + post.id"
|
||||
class="pinned-row"
|
||||
@click="handlePostClick(post)"
|
||||
>
|
||||
<span class="pinned-badge">置顶</span>
|
||||
<span class="pinned-title">{{ displayTitle(post) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分界线 -->
|
||||
<div v-if="pinnedList.length && normalList.length" class="list-divider">
|
||||
<span class="divider-line" />
|
||||
<span class="divider-text">全部帖子</span>
|
||||
<span class="divider-line" />
|
||||
</div>
|
||||
|
||||
<!-- 普通帖子 -->
|
||||
<PostItem
|
||||
v-for="post in normalList"
|
||||
: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 && normalList.length > 0" class="no-more">
|
||||
没有更多数据了
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<el-empty
|
||||
v-if="!loading && postList.length === 0"
|
||||
class="post-empty"
|
||||
description="暂无帖子"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 发布对话框 -->
|
||||
<PublishDialog
|
||||
v-model="showPublishDialog"
|
||||
@success="handlePublishSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { ChatDotRound, Loading, Plus } 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 totalDisplay = computed(() => {
|
||||
const n = total.value;
|
||||
if (n >= 10000) return `${(n / 10000).toFixed(1)}W`;
|
||||
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
|
||||
return String(n);
|
||||
});
|
||||
|
||||
const pinnedList = computed(() => postList.value.filter((p) => p.isPremium));
|
||||
|
||||
const normalList = computed(() => postList.value.filter((p) => !p.isPremium));
|
||||
|
||||
/** 置顶行展示标题:优先接口 title,否则从正文首行截取 */
|
||||
function displayTitle(post) {
|
||||
const tit = (post.title || "").trim();
|
||||
if (tit) {
|
||||
return tit.length <= 56 ? tit : `${tit.slice(0, 56)}…`;
|
||||
}
|
||||
const t = (post.content || "").trim();
|
||||
if (!t) return "无标题";
|
||||
const idx = t.indexOf("\n");
|
||||
const firstLine = idx === -1 ? t : t.slice(0, idx).trim();
|
||||
const line = firstLine || t.slice(0, 80);
|
||||
if (line.length <= 56) return line;
|
||||
return `${line.slice(0, 56)}…`;
|
||||
}
|
||||
|
||||
// 计算是否禁用滚动加载
|
||||
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,
|
||||
title: item.title || "",
|
||||
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">
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.post-list {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
// 贴吧式顶栏:圆角条 + 左图标标题统计 + 右胶囊按钮(外层页面仍为原深蓝网格背景)
|
||||
.bar-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
padding: 14px 18px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(
|
||||
160deg,
|
||||
rgba(16, 46, 96, 0.88) 0%,
|
||||
rgba(12, 34, 76, 0.82) 100%
|
||||
);
|
||||
border: 1px solid rgba(0, 163, 255, 0.28);
|
||||
box-shadow: inset 0 1px 0 rgba(0, 220, 255, 0.1),
|
||||
0 4px 20px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.bar-head-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.bar-logo {
|
||||
flex-shrink: 0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 2px 10px rgba(0, 100, 180, 0.2);
|
||||
|
||||
.el-icon {
|
||||
font-size: 26px;
|
||||
color: #00a3ff;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-head-text {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.bar-title {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
letter-spacing: 0.02em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.bar-meta {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.bar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bar-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 8px 18px;
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: rgba(230, 240, 255, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
.el-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.bar-pill-primary {
|
||||
border-color: rgba(0, 227, 255, 0.4);
|
||||
background: rgba(0, 60, 120, 0.35);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 100, 180, 0.45);
|
||||
box-shadow: 0 0 14px rgba(0, 163, 255, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
// 置顶缩略区(仅标题)
|
||||
.pinned-block {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.pinned-list {
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(220, 235, 255, 0.12);
|
||||
border: 1px solid rgba(0, 163, 255, 0.22);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.pinned-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background 0.15s ease;
|
||||
|
||||
& + .pinned-row {
|
||||
border-top: 1px solid rgba(0, 163, 255, 0.12);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 163, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.pinned-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 2px 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: $lt-cyan-mid;
|
||||
border: 1px solid rgba(0, 163, 255, 0.45);
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 40, 90, 0.35);
|
||||
}
|
||||
|
||||
.pinned-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $lt-text;
|
||||
line-height: 1.45;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
// 置顶与普通帖之间的分界
|
||||
.list-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 16px 0 14px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(0, 163, 255, 0.45),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.divider-text {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.posts-container {
|
||||
min-height: 400px;
|
||||
|
||||
:deep(.el-loading-mask) {
|
||||
background-color: rgba(0, 5, 16, 0.65);
|
||||
}
|
||||
|
||||
:deep(.el-loading-spinner .path) {
|
||||
stroke: #3db8ff;
|
||||
}
|
||||
}
|
||||
|
||||
.post-empty {
|
||||
padding: 48px 0;
|
||||
|
||||
:deep(.el-empty__description) {
|
||||
color: rgba(180, 200, 230, 0.65);
|
||||
}
|
||||
|
||||
:deep(.el-empty__image) {
|
||||
opacity: 0.85;
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
color: rgba(160, 185, 215, 0.75);
|
||||
font-size: 14px;
|
||||
gap: 8px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
color: #3db8ff;
|
||||
}
|
||||
}
|
||||
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: rgba(140, 165, 200, 0.55);
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
class="luntan-tech-dialog"
|
||||
title="发布帖子"
|
||||
width="60%"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<div style="overflow: auto; height: 60vh">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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 等子组件样式在各自内部
|
||||
::v-deep .form-item-box {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../styles/luntan-dialog-tech.scss";
|
||||
</style>
|
||||
@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
class="luntan-tech-dialog"
|
||||
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>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/luntan-dialog-tech.scss';
|
||||
</style>
|
||||
346
src/views/backOfficeSystem/luntan copy/components/UserCard.vue
Normal file
346
src/views/backOfficeSystem/luntan copy/components/UserCard.vue
Normal file
@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<div class="user-card">
|
||||
<div class="user-card-head">
|
||||
<div class="user-avatar">
|
||||
<div class="avatar-wrapper" @click="showAvatarDialog = true">
|
||||
<el-avatar :size="56" :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-card-head-text">
|
||||
<div class="name-row">
|
||||
<span class="nickname">{{ userInfo.nickname || '用户信息' }}</span>
|
||||
</div>
|
||||
<div class="sub-stats">内部论坛 · 已登录</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"
|
||||
class="luntan-tech-dialog"
|
||||
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">
|
||||
@import '../styles/luntan-tech.scss';
|
||||
|
||||
.user-card {
|
||||
border-radius: 4px;
|
||||
padding: 18px 16px 16px;
|
||||
@include lt-panel-frame;
|
||||
}
|
||||
|
||||
.user-card-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2px solid rgba(100, 180, 255, 0.35);
|
||||
|
||||
&:hover .avatar-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.avatar-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
border-radius: 50%;
|
||||
|
||||
.upload-icon {
|
||||
font-size: 22px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-head-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.name-row {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #f0f6ff;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.sub-stats {
|
||||
font-size: 12px;
|
||||
color: rgba(180, 200, 230, 0.55);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
padding: 6px 8px;
|
||||
margin-left: -8px;
|
||||
margin-right: -8px;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.06);
|
||||
|
||||
.edit-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
color: rgba(160, 185, 215, 0.55);
|
||||
min-width: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: rgba(220, 230, 245, 0.88);
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
margin-left: 6px;
|
||||
color: #5eb8ff;
|
||||
font-size: 14px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/luntan-dialog-tech.scss';
|
||||
</style>
|
||||
333
src/views/backOfficeSystem/luntan copy/index.vue
Normal file
333
src/views/backOfficeSystem/luntan copy/index.vue
Normal file
@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="luntan-container">
|
||||
<!-- 列表页:主列帖子 + 右侧信息栏 -->
|
||||
<template v-if="!showDetail">
|
||||
<div class="luntan-main">
|
||||
<PostList @openDetail="handleOpenDetail" />
|
||||
</div>
|
||||
<aside class="luntan-sidebar">
|
||||
<UserCard />
|
||||
<div class="hot-news-card" v-loading="hotLoading">
|
||||
<div class="hot-news-head">
|
||||
<span class="hot-news-title">热度消息</span>
|
||||
<span class="hot-news-badge">HOT</span>
|
||||
</div>
|
||||
<ul class="hot-news-list">
|
||||
<li
|
||||
v-for="(item, index) in hotList"
|
||||
:key="item.id"
|
||||
class="hot-news-item"
|
||||
@click="handleOpenDetail(item)"
|
||||
>
|
||||
<span class="hot-rank" :class="{ 'is-top': index < 3 }">{{
|
||||
index + 1
|
||||
}}</span>
|
||||
<span class="hot-item-title">{{ item.lineTitle }}</span>
|
||||
</li>
|
||||
<li
|
||||
v-if="!hotLoading && hotList.length === 0"
|
||||
class="hot-news-empty"
|
||||
>
|
||||
暂无热度内容
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<!-- 详情页 -->
|
||||
<template v-else>
|
||||
<div class="luntan-detail-wrap">
|
||||
<div class="luntan-detail">
|
||||
<PostDetail :post-id="currentPostId" @back="handleBack" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import UserCard from "./components/UserCard.vue";
|
||||
import PostList from "./components/PostList.vue";
|
||||
import PostDetail from "./components/PostDetail.vue";
|
||||
import { tbGsxtXxltSelectPage } from "@/api/tbGsxtXxltHf";
|
||||
import { setAddress } from "@/utils/tools";
|
||||
|
||||
const showDetail = ref(false);
|
||||
const currentPostId = ref(null);
|
||||
|
||||
const hotList = ref([]);
|
||||
const hotLoading = ref(false);
|
||||
|
||||
function lineTitleFromRecord(item) {
|
||||
const tit = (item.title || "").trim();
|
||||
if (tit) {
|
||||
return tit.length > 40 ? `${tit.slice(0, 40)}…` : tit;
|
||||
}
|
||||
const t = (item.content || "").trim();
|
||||
if (!t) return "无标题";
|
||||
const idx = t.indexOf("\n");
|
||||
const first = idx === -1 ? t : t.slice(0, idx).trim();
|
||||
const line = first || t.slice(0, 60);
|
||||
return line.length > 40 ? `${line.slice(0, 40)}…` : line;
|
||||
}
|
||||
|
||||
function mapHotPost(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
lineTitle: lineTitleFromRecord(item),
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
const loadHotNews = async () => {
|
||||
hotLoading.value = true;
|
||||
try {
|
||||
const res = await tbGsxtXxltSelectPage({
|
||||
pageCurrent: 1,
|
||||
pageSize: 12
|
||||
});
|
||||
const records = res.records || [];
|
||||
hotList.value = records.map(mapHotPost);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
hotList.value = [];
|
||||
} finally {
|
||||
hotLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadHotNews();
|
||||
});
|
||||
|
||||
const handleOpenDetail = (post) => {
|
||||
currentPostId.value = post.id;
|
||||
showDetail.value = true;
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
showDetail.value = false;
|
||||
currentPostId.value = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/luntan-tech.scss";
|
||||
|
||||
.luntan-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
padding: 20px 24px;
|
||||
min-height: calc(100vh - 60px);
|
||||
max-height: calc(100vh - 60px);
|
||||
overflow: hidden;
|
||||
background: linear-gradient(165deg, $lt-bg 0%, $lt-bg-soft 42%, #0a1830 100%);
|
||||
box-sizing: border-box;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: linear-gradient(
|
||||
rgba(0, 163, 255, 0.04) 1px,
|
||||
transparent 1px
|
||||
),
|
||||
linear-gradient(90deg, rgba(0, 163, 255, 0.04) 1px, transparent 1px);
|
||||
background-size: 32px 32px;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.luntan-main {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.luntan-sidebar {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
height: calc(100vh - 100px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hot-news-card {
|
||||
position: relative;
|
||||
// flex: 1;
|
||||
height: calc(80% - 100px);
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
@include lt-panel-frame;
|
||||
background: linear-gradient(
|
||||
165deg,
|
||||
rgba(14, 40, 82, 0.9) 0%,
|
||||
rgba(10, 28, 58, 0.88) 100%
|
||||
) !important;
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.el-loading-mask) {
|
||||
background-color: rgba(4, 12, 28, 0.55);
|
||||
}
|
||||
|
||||
:deep(.el-loading-spinner .path) {
|
||||
stroke: $lt-cyan-mid;
|
||||
}
|
||||
}
|
||||
|
||||
.hot-news-head {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 14px 16px 12px;
|
||||
border-bottom: 1px solid rgba(0, 163, 255, 0.22);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(0, 163, 255, 0.1) 0%,
|
||||
transparent 70%
|
||||
);
|
||||
}
|
||||
|
||||
.hot-news-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
letter-spacing: 0.08em;
|
||||
text-shadow: 0 0 14px rgba(0, 227, 255, 0.35);
|
||||
}
|
||||
|
||||
.hot-news-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 3px 10px;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.12em;
|
||||
color: #1a0a00;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(180deg, #ffb347 0%, #ff9900 100%);
|
||||
border: 1px solid rgba(255, 200, 120, 0.5);
|
||||
box-shadow: 0 0 12px rgba(255, 153, 0, 0.35);
|
||||
}
|
||||
|
||||
.hot-news-list {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
padding: 8px 10px 12px;
|
||||
list-style: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.hot-news-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 10px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
& + .hot-news-item {
|
||||
border-top: 1px solid rgba(0, 163, 255, 0.1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 163, 255, 0.1);
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 227, 255, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
flex-shrink: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: rgba(210, 225, 245, 0.92);
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 40, 80, 0.45);
|
||||
border: 1px solid rgba(0, 163, 255, 0.2);
|
||||
|
||||
&.is-top {
|
||||
color: #1a0a00;
|
||||
background: linear-gradient(180deg, #ffd699 0%, #ff9900 100%);
|
||||
border-color: rgba(255, 180, 80, 0.55);
|
||||
box-shadow: 0 0 8px rgba(255, 153, 0, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
.hot-item-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: rgba(236, 245, 255, 0.94);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hot-news-item:hover .hot-item-title {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.hot-news-empty {
|
||||
padding: 28px 12px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: rgba(200, 218, 240, 0.82);
|
||||
list-style: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.luntan-detail-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.luntan-detail {
|
||||
height: 83vh;
|
||||
flex: 1;
|
||||
border-radius: 4px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@include lt-panel-frame;
|
||||
box-shadow: $lt-glow-strong, 0 12px 40px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 非 scoped:el-image-viewer 通过 Teleport 挂到 body,需全局选择器 -->
|
||||
<style lang="scss">
|
||||
@import "./styles/luntan-tech.scss";
|
||||
</style>
|
||||
@ -0,0 +1,104 @@
|
||||
@import "./luntan-tech.scss";
|
||||
@import "./luntan-v3emoji-tech.scss";
|
||||
|
||||
.luntan-tech-dialog.el-dialog {
|
||||
background: rgba(8, 20, 48, 0.98) !important;
|
||||
border: 1px solid $lt-border;
|
||||
border-radius: 4px;
|
||||
box-shadow: $lt-glow-strong, 0 16px 48px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__header {
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
padding: 14px 18px;
|
||||
margin: 0;
|
||||
background: rgba(8, 20, 48, 0.98) !important;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__title {
|
||||
color: $lt-text;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__headerbtn .el-dialog__close {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__headerbtn:hover .el-dialog__close {
|
||||
color: $lt-cyan;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__body {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__footer {
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
padding: 12px 18px;
|
||||
background: rgba(8, 20, 48, 0.98) !important;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-form-item__label {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-input__wrapper {
|
||||
background: rgba(10, 28, 58, 0.85) !important;
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.28) inset !important;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.4) inset !important;
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.55) inset,
|
||||
0 0 14px rgba(0, 163, 255, 0.22) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-input__inner {
|
||||
color: $lt-text-dim;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-input__count,
|
||||
.luntan-tech-dialog .el-input__count .el-input__count-inner {
|
||||
background: transparent !important;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-textarea__inner {
|
||||
background: rgba(10, 28, 58, 0.9) !important;
|
||||
color: $lt-text-dim;
|
||||
border: 1px solid rgba(0, 227, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 0 20px rgba(20, 80, 140, 0.2);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-button--default {
|
||||
background: rgba(15, 40, 75, 0.65);
|
||||
border-color: rgba(0, 163, 255, 0.35);
|
||||
color: $lt-text-dim;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-button--primary {
|
||||
background: linear-gradient(180deg, #00a3ff 0%, #0066bb 100%);
|
||||
border-color: rgba(0, 227, 255, 0.5);
|
||||
box-shadow: 0 0 16px rgba(0, 163, 255, 0.4);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-upload--picture-card {
|
||||
background: rgba(10, 28, 58, 0.75) !important;
|
||||
border-color: rgba(0, 227, 255, 0.35) !important;
|
||||
|
||||
.el-icon {
|
||||
color: $lt-cyan;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(0, 227, 255, 0.55) !important;
|
||||
box-shadow: 0 0 14px rgba(0, 163, 255, 0.25);
|
||||
}
|
||||
}
|
||||
103
src/views/backOfficeSystem/luntan copy/styles/luntan-tech.scss
Normal file
103
src/views/backOfficeSystem/luntan copy/styles/luntan-tech.scss
Normal file
@ -0,0 +1,103 @@
|
||||
// 论坛模块 — 大屏科技风主题变量
|
||||
$lt-bg: #0a1628;
|
||||
$lt-bg-soft: #0d1f3c;
|
||||
$lt-cyan: #00e5ff;
|
||||
$lt-cyan-mid: #00a3ff;
|
||||
$lt-panel: rgba(10, 30, 60, 0.85);
|
||||
$lt-panel-soft: rgba(8, 24, 52, 0.65);
|
||||
$lt-border: rgba(0, 227, 255, 0.38);
|
||||
$lt-border-dim: rgba(0, 163, 255, 0.2);
|
||||
$lt-text: #e8f4ff;
|
||||
$lt-text-dim: rgba(140, 200, 230, 0.78);
|
||||
$lt-text-muted: rgba(120, 170, 210, 0.55);
|
||||
$lt-glow: 0 0 18px rgba(0, 163, 255, 0.35);
|
||||
$lt-glow-strong: 0 0 28px rgba(0, 227, 255, 0.28);
|
||||
|
||||
@mixin lt-panel-frame {
|
||||
background: $lt-panel;
|
||||
border: 1px solid $lt-border;
|
||||
box-shadow: $lt-glow, inset 0 1px 0 rgba(0, 220, 255, 0.12);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
@mixin lt-panel-soft-bg {
|
||||
background: $lt-panel-soft;
|
||||
border: 1px solid $lt-border-dim;
|
||||
box-shadow: inset 0 1px 0 rgba(0, 220, 255, 0.08);
|
||||
}
|
||||
|
||||
@mixin lt-corner-brackets {
|
||||
position: relative;
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-color: $lt-cyan-mid;
|
||||
border-style: solid;
|
||||
pointer-events: none;
|
||||
opacity: 0.85;
|
||||
}
|
||||
&::before {
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
border-width: 2px 0 0 2px;
|
||||
box-shadow: -1px -1px 8px rgba(0, 227, 255, 0.4);
|
||||
}
|
||||
&::after {
|
||||
bottom: -1px;
|
||||
right: -1px;
|
||||
border-width: 0 2px 2px 0;
|
||||
box-shadow: 1px 1px 8px rgba(0, 227, 255, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
// el-image-viewer 全屏预览覆盖 — Teleport 到 body,全局生效
|
||||
.el-image-viewer__wrapper {
|
||||
background: rgba(4, 10, 24, 0.95) !important;
|
||||
backdrop-filter: blur(4px);
|
||||
|
||||
.el-image-viewer__mask {
|
||||
background: rgba(0, 10, 30, 0.78) !important;
|
||||
}
|
||||
|
||||
.el-image-viewer__btn {
|
||||
color: $lt-cyan;
|
||||
border: 1px solid rgba(0, 227, 255, 0.3);
|
||||
background: rgba(10, 28, 58, 0.65);
|
||||
box-shadow: 0 0 10px rgba(0, 120, 200, 0.2);
|
||||
|
||||
&:hover {
|
||||
background: rgba(15, 45, 80, 0.88);
|
||||
border-color: rgba(0, 227, 255, 0.55);
|
||||
}
|
||||
|
||||
&.el-image-viewer__close {
|
||||
top: 24px;
|
||||
right: 28px;
|
||||
}
|
||||
|
||||
&.el-image-viewer__prev {
|
||||
left: 24px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&.el-image-viewer__next {
|
||||
right: 24px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.el-image-viewer__canvas {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
@import './luntan-tech.scss';
|
||||
|
||||
// vue3-emoji(dist/style.css)默认浅色/白底,与论坛科技风统一
|
||||
@mixin lt-v3-emoji-vars {
|
||||
--V3Emoji-backgroundColor: rgba(10, 28, 58, 0.96);
|
||||
--V3Emoji-hoverColor: rgba(0, 100, 160, 0.22);
|
||||
--V3Emoji-activeColor: rgba(0, 130, 200, 0.3);
|
||||
--V3Emoji-fontColor: #{$lt-text};
|
||||
--V3Emoji-borderColor: #{rgba(0, 227, 255, 0.38)};
|
||||
--V3Emoji-borderFocusColor: #{$lt-cyan};
|
||||
--V3Emoji-shadowColor: rgba(0, 40, 90, 0.45);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog,
|
||||
.comment-list .reply-input-box,
|
||||
.luntan-tech-dialog .emoji-row {
|
||||
@include lt-v3-emoji-vars;
|
||||
}
|
||||
|
||||
// 组件内未使用 CSS 变量的原生 input / textarea(仍为白底)
|
||||
.luntan-tech-dialog [class*='emojiInput'] input,
|
||||
.comment-list .reply-input-box [class*='emojiInput'] input {
|
||||
background: rgba(10, 28, 58, 0.92) !important;
|
||||
color: $lt-text-dim !important;
|
||||
border-color: rgba(0, 227, 255, 0.35) !important;
|
||||
box-shadow: inset 0 0 12px rgba(0, 50, 100, 0.25) !important;
|
||||
|
||||
&::placeholder {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.luntan-tech-dialog [class*='emojiTextarea'] textarea,
|
||||
.comment-list .reply-input-box [class*='emojiTextarea'] textarea {
|
||||
background: rgba(10, 28, 58, 0.92) !important;
|
||||
color: $lt-text-dim !important;
|
||||
border-color: rgba(0, 227, 255, 0.35) !important;
|
||||
box-shadow: inset 0 0 12px rgba(0, 50, 100, 0.25) !important;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog [class*='emojiContainerOpenBtn'],
|
||||
.luntan-tech-dialog [class*='emojiTextareaOpenBtn'],
|
||||
.comment-list .reply-input-box [class*='emojiContainerOpenBtn'],
|
||||
.comment-list .reply-input-box [class*='emojiTextareaOpenBtn'] {
|
||||
color: $lt-cyan !important;
|
||||
filter: drop-shadow(0 0 6px rgba(0, 227, 255, 0.35));
|
||||
}
|
||||
|
||||
// 表情面板可能 teleport / 挂到 body,需单独写选择器
|
||||
[class*='V3Emoji-vue'][class*='__pollup___'],
|
||||
[class*='PollUp-vue'][class*='__pollup___'] {
|
||||
@include lt-v3-emoji-vars;
|
||||
}
|
||||
|
||||
[id='EmojiItem'],
|
||||
[id*='EmojiItem'] {
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 163, 255, 0.45) !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(8, 20, 48, 0.88) !important;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<el-dialog :model-value="modelValue" center width="500px" :destroy-on-close="true" :title="title" @close="close"
|
||||
:close-on-click-modal="false">
|
||||
<el-dialog
|
||||
class="luntan-tech-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">
|
||||
@ -133,11 +141,13 @@ const close = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/luntan-tech.scss';
|
||||
|
||||
.avatar-upload-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
padding: 12px 0 8px;
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
@ -145,12 +155,13 @@ const close = () => {
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2px solid #e8e8e8;
|
||||
border: 2px solid rgba(0, 227, 255, 0.4);
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f5f7fa;
|
||||
background: rgba(10, 30, 60, 0.6);
|
||||
box-shadow: 0 0 20px rgba(0, 120, 200, 0.2);
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
@ -163,11 +174,12 @@ const close = () => {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 8px;
|
||||
color: rgba(0, 227, 255, 0.45);
|
||||
}
|
||||
|
||||
span {
|
||||
@ -185,15 +197,22 @@ const close = () => {
|
||||
|
||||
.avatar-uploader {
|
||||
margin-bottom: 16px;
|
||||
|
||||
:deep(.el-button--primary) {
|
||||
background: linear-gradient(180deg, #00a3ff 0%, #0066bb 100%);
|
||||
border-color: rgba(0, 227, 255, 0.5);
|
||||
box-shadow: 0 0 12px rgba(0, 163, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.upload-tips {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
background: rgba(10, 30, 60, 0.65);
|
||||
border: 1px solid $lt-border-dim;
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
@ -201,3 +220,7 @@ const close = () => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/luntan-dialog-tech.scss';
|
||||
</style>
|
||||
|
||||
@ -28,12 +28,17 @@
|
||||
</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}条回复` }}
|
||||
<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>
|
||||
@ -45,13 +50,27 @@
|
||||
<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" />
|
||||
<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;" />
|
||||
<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
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="submitReply(comment)"
|
||||
>
|
||||
回复
|
||||
</el-button>
|
||||
</div>
|
||||
@ -60,9 +79,24 @@
|
||||
</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">
|
||||
<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>
|
||||
|
||||
@ -90,7 +124,10 @@
|
||||
</el-icon>
|
||||
<span v-if="reply.likeCount">{{ reply.likeCount }}</span>
|
||||
</div> -->
|
||||
<div class="action-btn" @click="handleReplyToReply(reply, comment)">
|
||||
<div
|
||||
class="action-btn"
|
||||
@click="handleReplyToReply(reply, comment)"
|
||||
>
|
||||
回复
|
||||
</div>
|
||||
</div>
|
||||
@ -98,16 +135,34 @@
|
||||
|
||||
<!-- 二级回复输入框 -->
|
||||
<transition name="slide-fade">
|
||||
<div v-if="activeReplyId === reply.id" class="reply-input-box">
|
||||
<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" />
|
||||
<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;" />
|
||||
<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 size="small" @click="cancelReply"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="submitReply(comment, reply)"
|
||||
>
|
||||
回复
|
||||
</el-button>
|
||||
</div>
|
||||
@ -124,122 +179,128 @@
|
||||
</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'
|
||||
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: '活动'
|
||||
}
|
||||
"Smileys & Emotion": "笑脸&表情",
|
||||
"Food & Drink": "食物&饮料",
|
||||
"Animals & Nature": "动物&自然",
|
||||
"Travel & Places": "旅行&地点",
|
||||
"People & Body": "人物&身体",
|
||||
Objects: "物品",
|
||||
Symbols: "符号",
|
||||
Flags: "旗帜",
|
||||
Activities: "活动"
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
comments: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}, replyTo: {
|
||||
},
|
||||
replyTo: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['reply', 'like'])
|
||||
const emit = defineEmits(["reply", "like"]);
|
||||
|
||||
const activeReplyId = ref(null)
|
||||
const replyContent = ref('')
|
||||
const activeReplyId = ref(null);
|
||||
const replyContent = ref("");
|
||||
|
||||
const getTagType = (tag) => {
|
||||
const tagMap = {
|
||||
'户外活动部': 'success',
|
||||
'校长': 'warning',
|
||||
'校本部': 'info'
|
||||
}
|
||||
return tagMap[tag] || 'info'
|
||||
}
|
||||
户外活动部: "success",
|
||||
校长: "warning",
|
||||
校本部: "info"
|
||||
};
|
||||
return tagMap[tag] || "info";
|
||||
};
|
||||
|
||||
const handleLike = (comment) => {
|
||||
emit('like', comment)
|
||||
}
|
||||
emit("like", comment);
|
||||
};
|
||||
|
||||
const handleReply = (comment) => {
|
||||
emit('reply')
|
||||
activeReplyId.value = comment.id
|
||||
replyContent.value = ''
|
||||
}
|
||||
emit("reply");
|
||||
activeReplyId.value = comment.id;
|
||||
replyContent.value = "";
|
||||
};
|
||||
|
||||
const handleReplyToReply = (reply, parentComment) => {
|
||||
emit('reply')
|
||||
activeReplyId.value = reply.id
|
||||
replyContent.value = `@${reply.userName}:`
|
||||
}
|
||||
emit("reply");
|
||||
activeReplyId.value = reply.id;
|
||||
replyContent.value = `@${reply.userName}:`;
|
||||
};
|
||||
|
||||
const onEmojiClick = (emoji) => {
|
||||
replyContent.value += emoji
|
||||
}
|
||||
replyContent.value += emoji;
|
||||
};
|
||||
|
||||
const cancelReply = () => {
|
||||
activeReplyId.value = null
|
||||
replyContent.value = ''
|
||||
}
|
||||
activeReplyId.value = null;
|
||||
replyContent.value = "";
|
||||
};
|
||||
|
||||
// 切换回复列表的展开/收起
|
||||
const toggleReplies = (comment) => {
|
||||
if (!comment.showReplies) {
|
||||
comment.showReplies = true
|
||||
comment.showReplies = true;
|
||||
} else {
|
||||
comment.showReplies = false
|
||||
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 || '',
|
||||
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 || ''
|
||||
}
|
||||
}
|
||||
ssbm: reply.ssbm || "",
|
||||
replyToUser: reply.replyToUser || reply.sjhfrxm || ""
|
||||
};
|
||||
};
|
||||
|
||||
const submitReply = async (parentComment, replyToComment = null) => {
|
||||
console.log(parentComment);
|
||||
|
||||
if (!replyContent.value.trim()) {
|
||||
ElMessage.warning('请输入回复内容')
|
||||
return
|
||||
ElMessage.warning("请输入回复内容");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const ltmasg = getItem("ltmasg")
|
||||
let pureContent = replyContent.value
|
||||
if (pureContent.startsWith('@') && pureContent.includes(':')) {
|
||||
const colonIndex = pureContent.indexOf(':')
|
||||
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()
|
||||
pureContent = pureContent.substring(colonIndex + 1).trim();
|
||||
}
|
||||
}
|
||||
if (!pureContent) {
|
||||
ElMessage.warning('请输入回复内容')
|
||||
return
|
||||
ElMessage.warning("请输入回复内容");
|
||||
return;
|
||||
}
|
||||
const targetReply = replyToComment || null
|
||||
const targetReply = replyToComment || null;
|
||||
const newReply = {
|
||||
hfnr: pureContent,
|
||||
hfrsfzh: ltmasg.sfzh,
|
||||
@ -248,14 +309,18 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
ltid: props.replyTo.id,
|
||||
sfyjhf: 0,
|
||||
sjhfid: parentComment.id,
|
||||
sjhfrxm: targetReply ? targetReply.userName : ''
|
||||
}
|
||||
sjhfrxm: targetReply ? targetReply.userName : ""
|
||||
};
|
||||
try {
|
||||
const res = await tbGsxtXxltHfSave(newReply)
|
||||
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)
|
||||
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);
|
||||
@ -266,24 +331,32 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
// parentComment.replies.push(newReply)
|
||||
|
||||
// 自动展开回复列表
|
||||
parentComment.showReplies = true
|
||||
ElMessage.success('回复成功')
|
||||
cancelReply()
|
||||
parentComment.showReplies = true;
|
||||
ElMessage.success("回复成功");
|
||||
cancelReply();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
ElMessage.error('回复失败')
|
||||
ElMessage.error("回复失败");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.comment-list {
|
||||
background: #ffffff;
|
||||
|
||||
.comment-item {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 18px;
|
||||
padding-bottom: 18px;
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,6 +369,8 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
|
||||
.comment-avatar {
|
||||
flex-shrink: 0;
|
||||
border: 1px solid lt-blue(0.28);
|
||||
box-shadow: 0 0 8px lt-blue(0.12);
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
@ -312,18 +387,19 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
color: $lt-text;
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #606266;
|
||||
line-height: 1.65;
|
||||
color: $lt-text-dim;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
}
|
||||
@ -336,7 +412,7 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
|
||||
.comment-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.comment-actions {
|
||||
@ -349,16 +425,17 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
color: $lt-bar-blue-end;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #409eff;
|
||||
color: $lt-bar-blue-end;
|
||||
}
|
||||
|
||||
&.toggle-btn {
|
||||
@ -375,18 +452,30 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
.reply-input-box {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
border-radius: 4px;
|
||||
@include lt-panel-soft-bg;
|
||||
}
|
||||
|
||||
.reply-label {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
color: $lt-text-muted;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.reply-textarea {
|
||||
margin-bottom: 8px;
|
||||
|
||||
:deep(.el-textarea__inner) {
|
||||
background: rgba(10, 28, 58, 0.88) !important;
|
||||
color: $lt-text-dim;
|
||||
border: 1px solid rgba(0, 227, 255, 0.28);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 12px rgba(0, 80, 140, 0.15) inset;
|
||||
|
||||
&::placeholder {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-actions {
|
||||
@ -398,12 +487,29 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
.reply-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.el-button) {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
:deep(.el-button--default) {
|
||||
background: rgba(0, 40, 70, 0.5);
|
||||
border-color: rgba(0, 163, 255, 0.35);
|
||||
color: $lt-text-dim;
|
||||
}
|
||||
|
||||
:deep(.el-button--primary) {
|
||||
background: linear-gradient(180deg, #00a3ff 0%, #0066bb 100%);
|
||||
border-color: rgba(0, 227, 255, 0.45);
|
||||
box-shadow: 0 0 12px rgba(0, 163, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.replies-list {
|
||||
margin-top: 16px;
|
||||
margin-top: 14px;
|
||||
padding-left: 12px;
|
||||
border-left: 2px solid #f0f0f0;
|
||||
border-left: 2px solid lt-blue(0.22);
|
||||
box-shadow: -2px 0 12px lt-blue(0.06);
|
||||
}
|
||||
|
||||
.reply-item {
|
||||
@ -419,6 +525,7 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
|
||||
.reply-avatar {
|
||||
flex-shrink: 0;
|
||||
border: 1px solid lt-blue(0.22);
|
||||
}
|
||||
|
||||
.reply-content {
|
||||
@ -432,18 +539,19 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.reply-text {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #606266;
|
||||
color: $lt-text-dim;
|
||||
margin-bottom: 6px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.reply-to {
|
||||
color: #409eff;
|
||||
color: $lt-bar-btn-text;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
@ -455,7 +563,7 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
|
||||
.reply-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.reply-actions {
|
||||
@ -480,9 +588,15 @@ const submitReply = async (parentComment, replyToComment = null) => {
|
||||
.author-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
background: lt-blue(0.1);
|
||||
color: $lt-bar-btn-text;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
border: 1px solid lt-blue(0.25);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../styles/luntan-v3emoji-tech.scss";
|
||||
</style>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="post-detail">
|
||||
<div class="post-detail luntan-tech-detail">
|
||||
<!-- 头部 -->
|
||||
<div class="detail-header">
|
||||
<el-button text @click="handleBack">
|
||||
<el-button class="detail-back-btn" @click="handleBack">
|
||||
<el-icon>
|
||||
<ArrowLeft />
|
||||
</el-icon>
|
||||
@ -13,15 +13,18 @@
|
||||
|
||||
<!-- 帖子内容 -->
|
||||
<div class="post-main">
|
||||
<div class="premium-badge" v-if="postData.isPremium">精品</div>
|
||||
<div class="premium-badge" v-if="postData.isPremium">置顶</div>
|
||||
|
||||
<div class="post-author">
|
||||
<el-avatar :size="50" :src="postData.userAvatar">
|
||||
<el-avatar :size="50" :src="postData.userAvatar" class="author-avatar">
|
||||
<img src="@/assets/images/mr.png" />
|
||||
</el-avatar>
|
||||
<div class="author-info">
|
||||
<div class="author-name-row">
|
||||
<span class="author-name">{{ postData.userName }}</span>
|
||||
<span v-if="postData.userTag" class="level-badge">{{
|
||||
postData.userTag
|
||||
}}</span>
|
||||
<div v-if="postData.ssbm" class="author-tag">
|
||||
{{ postData.ssbm }}
|
||||
</div>
|
||||
@ -33,9 +36,22 @@
|
||||
<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">
|
||||
<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
|
||||
:preview-teleported="true"
|
||||
:src="img"
|
||||
fit="cover"
|
||||
:preview-src-list="postData.images"
|
||||
:initial-index="index"
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-error">
|
||||
<el-icon>
|
||||
@ -68,7 +84,11 @@
|
||||
<div class="comment-section">
|
||||
<!-- Tab切换 -->
|
||||
<div class="comment-tabs">
|
||||
<div class="tab-item" :class="{ active: activeTab === 'all' }" @click="activeTab = 'all'">
|
||||
<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'">
|
||||
@ -89,225 +109,272 @@
|
||||
|
||||
<!-- 评论列表 -->
|
||||
|
||||
<CommentList :comments="filteredComments" @reply="handleReply" :replyTo="replyTo" @like="handleCommentLike" />
|
||||
<CommentList
|
||||
:comments="filteredComments"
|
||||
@reply="handleReply"
|
||||
:replyTo="replyTo"
|
||||
@like="handleCommentLike"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 回复弹窗 -->
|
||||
<ReplyDialog v-model="showReplyDialog" :reply-to="replyTo" @success="handleReplySuccess" />
|
||||
<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'
|
||||
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 emit = defineEmits(["back"]);
|
||||
|
||||
const activeTab = ref('all')
|
||||
const showReplyDialog = ref(false)
|
||||
const replyTo = ref(null)
|
||||
const loading = ref(false)
|
||||
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: '',
|
||||
userName: "",
|
||||
userAvatar: "",
|
||||
userTag: "",
|
||||
publishTime: "",
|
||||
content: "",
|
||||
images: [],
|
||||
commentCount: 0,
|
||||
likeCount: 0,
|
||||
isPremium: false,
|
||||
isLiked: false
|
||||
})
|
||||
});
|
||||
|
||||
// 评论数据
|
||||
const comments = ref([])
|
||||
const comments = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
loadPostDetail()
|
||||
})
|
||||
loadPostDetail();
|
||||
});
|
||||
|
||||
const loadPostDetail = async () => {
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await tbGsxtXxltHfid(props.postId)
|
||||
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)) : [],
|
||||
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,
|
||||
}
|
||||
ssbm: res.ssbm
|
||||
};
|
||||
|
||||
// 设置评论数据
|
||||
if (res.replyList && res.replyList.length > 0) {
|
||||
comments.value = res.replyList.map(item => ({
|
||||
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 || '',
|
||||
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,
|
||||
})) : []
|
||||
}))
|
||||
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('加载详情失败')
|
||||
console.error("加载详情失败", error);
|
||||
ElMessage.error("加载详情失败");
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const filteredComments = computed(() => {
|
||||
if (activeTab.value === 'author') {
|
||||
return comments.value.filter(c => c.userName === postData.value.userName)
|
||||
if (activeTab.value === "author") {
|
||||
return comments.value.filter((c) => c.userName === postData.value.userName);
|
||||
}
|
||||
console.log(comments.value);
|
||||
|
||||
return comments.value
|
||||
})
|
||||
return comments.value;
|
||||
});
|
||||
|
||||
const getTagType = (tag) => {
|
||||
const tagMap = {
|
||||
'户外活动部': 'success',
|
||||
'校长': 'warning',
|
||||
'校本部': 'info'
|
||||
}
|
||||
return tagMap[tag] || 'info'
|
||||
}
|
||||
户外活动部: "success",
|
||||
校长: "warning",
|
||||
校本部: "info"
|
||||
};
|
||||
return tagMap[tag] || "info";
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
emit('back')
|
||||
}
|
||||
emit("back");
|
||||
};
|
||||
|
||||
const handleLike = () => {
|
||||
postData.value.isLiked = !postData.value.isLiked
|
||||
postData.value.likeCount += postData.value.isLiked ? 1 : -1
|
||||
ElMessage.success(postData.value.isLiked ? '点赞成功' : '取消点赞')
|
||||
}
|
||||
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,
|
||||
}
|
||||
...postData.value
|
||||
};
|
||||
// showReplyDialog.value = true
|
||||
}
|
||||
};
|
||||
const replyToData = () => {
|
||||
replyTo.value = {
|
||||
...postData.value,
|
||||
}
|
||||
showReplyDialog.value = true
|
||||
}
|
||||
...postData.value
|
||||
};
|
||||
showReplyDialog.value = true;
|
||||
};
|
||||
const handleCommentLike = (comment) => {
|
||||
comment.isLiked = !comment.isLiked
|
||||
comment.likeCount += comment.isLiked ? 1 : -1
|
||||
}
|
||||
comment.isLiked = !comment.isLiked;
|
||||
comment.likeCount += comment.isLiked ? 1 : -1;
|
||||
};
|
||||
|
||||
const handleReplySuccess = () => {
|
||||
ElMessage.success('回复成功')
|
||||
loadPostDetail()
|
||||
}
|
||||
ElMessage.success("回复成功");
|
||||
loadPostDetail();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.post-detail {
|
||||
background: #f5f7fa;
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.luntan-tech-detail {
|
||||
background: transparent;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
background: #fff;
|
||||
padding: 16px 20px;
|
||||
padding: 14px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid lt-blue(0.12);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
lt-blue-light(0.14) 0%,
|
||||
lt-blue(0.07) 48%,
|
||||
transparent 100%
|
||||
);
|
||||
|
||||
:deep(.detail-back-btn.el-button) {
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
background: #ffffff !important;
|
||||
border: 1px solid lt-blue(0.22) !important;
|
||||
color: $lt-bar-btn-text !important;
|
||||
box-shadow: 0 1px 4px lt-blue(0.12);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $lt-bar-blue-end !important;
|
||||
border-color: lt-blue(0.4) !important;
|
||||
background: #f8fafc !important;
|
||||
box-shadow: 0 2px 10px lt-blue(0.18);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.detail-back-btn .el-icon) {
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
color: $lt-text;
|
||||
margin-right: 60px;
|
||||
letter-spacing: 0.08em;
|
||||
text-shadow: 0 0 14px lt-blue(0.18);
|
||||
}
|
||||
}
|
||||
|
||||
.post-main {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
margin-bottom: 12px;
|
||||
padding: 20px 18px 18px;
|
||||
margin: 12px 12px 0;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
@include lt-panel-soft-bg;
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
background: #fff;
|
||||
padding: 16px 20px 40px 20px;
|
||||
padding: 8px 12px 32px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.post-main {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
margin-bottom: 12px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.premium-badge {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
background: linear-gradient(135deg, #ff9a56 0%, #ff6b6b 100%);
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
$lt-bar-blue-start 0%,
|
||||
$lt-bar-blue-end 100%
|
||||
);
|
||||
color: #fff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
box-shadow: 0 2px 10px lt-blue(0.3);
|
||||
}
|
||||
|
||||
.post-author {
|
||||
@ -317,6 +384,11 @@ const handleReplySuccess = () => {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.author-avatar {
|
||||
border: 2px solid lt-blue(0.35);
|
||||
box-shadow: 0 0 12px lt-blue(0.22);
|
||||
}
|
||||
|
||||
.author-info {
|
||||
flex: 1;
|
||||
padding-top: 2px;
|
||||
@ -325,34 +397,46 @@ const handleReplySuccess = () => {
|
||||
.author-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.author-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
background: lt-blue(0.1);
|
||||
color: $lt-bar-blue-end;
|
||||
font-weight: 600;
|
||||
border: 1px solid lt-blue(0.28);
|
||||
}
|
||||
|
||||
.author-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
background: lt-blue(0.08);
|
||||
color: $lt-bar-btn-text;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
border: 1px solid lt-blue(0.25);
|
||||
}
|
||||
|
||||
.publish-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.post-content-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
color: #303133;
|
||||
line-height: 1.85;
|
||||
color: $lt-text-dim;
|
||||
margin-bottom: 16px;
|
||||
word-break: break-word;
|
||||
}
|
||||
@ -367,8 +451,10 @@ const handleReplySuccess = () => {
|
||||
.image-item {
|
||||
width: 200px;
|
||||
height: 140px;
|
||||
border-radius: 8px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
border: 1px solid $lt-border-dim;
|
||||
box-shadow: 0 0 12px rgba(0, 100, 160, 0.2);
|
||||
|
||||
:deep(.el-image) {
|
||||
width: 100%;
|
||||
@ -382,8 +468,8 @@ const handleReplySuccess = () => {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
color: #c0c4cc;
|
||||
background: rgba(10, 30, 60, 0.6);
|
||||
color: $lt-text-muted;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@ -391,24 +477,20 @@ const handleReplySuccess = () => {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #909399;
|
||||
color: $lt-text-muted;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #409eff;
|
||||
color: $lt-cyan;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
@ -416,40 +498,34 @@ const handleReplySuccess = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
margin-bottom: 14px;
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 12px 16px;
|
||||
padding: 12px 14px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
color: $lt-text-muted;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&.active {
|
||||
color: #409eff;
|
||||
font-weight: 500;
|
||||
color: $lt-cyan;
|
||||
font-weight: 600;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: #409eff;
|
||||
background: linear-gradient(90deg, transparent, $lt-cyan, transparent);
|
||||
box-shadow: 0 0 10px rgba(0, 227, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -461,11 +537,28 @@ const handleReplySuccess = () => {
|
||||
}
|
||||
|
||||
.top-input {
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
cursor: pointer;
|
||||
background: rgba(10, 30, 60, 0.65) !important;
|
||||
border-radius: 4px !important;
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.28) inset,
|
||||
0 0 16px rgba(0, 100, 180, 0.15) !important;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.45) inset,
|
||||
0 0 20px rgba(0, 163, 255, 0.25) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
color: $lt-text-dim;
|
||||
|
||||
&::placeholder {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div class="post-item" @click="handleClick">
|
||||
<!-- 精品标签 -->
|
||||
<div class="premium-badge" v-if="post.isPremium">精品</div>
|
||||
<div class="premium-badge" v-if="post.isPremium">置顶</div>
|
||||
<div class="post-main-content">
|
||||
<el-avatar :size="50" :src="post.userAvatar" class="post-avatar">
|
||||
<el-avatar :size="44" :src="post.userAvatar" class="post-avatar">
|
||||
<img src="@/assets/images/mr.png" />
|
||||
</el-avatar>
|
||||
|
||||
@ -11,6 +10,9 @@
|
||||
<div class="post-header">
|
||||
<div class="user-name-row">
|
||||
<span class="user-name">{{ post.userName }}</span>
|
||||
<span v-if="post.userTag" class="level-badge">{{
|
||||
post.userTag
|
||||
}}</span>
|
||||
<div v-if="post.ssbm" class="author-tag">
|
||||
{{ post.ssbm }}
|
||||
</div>
|
||||
@ -18,13 +20,37 @@
|
||||
<div class="post-time">{{ post.publishTime }}</div>
|
||||
</div>
|
||||
|
||||
<div class="post-content">
|
||||
<div class="post-text">{{ post.content }}</div>
|
||||
<div class="post-content" v-if="postTitle || bodySource">
|
||||
<div v-if="postTitle" class="post-title">{{ postTitle }}</div>
|
||||
<div v-if="bodySource" class="post-text-wrap">
|
||||
<div class="post-text">{{ displayBody }}</div>
|
||||
<span
|
||||
v-if="showFullLink"
|
||||
class="full-text-link"
|
||||
@click.stop="handleClick"
|
||||
>全文</span
|
||||
>
|
||||
</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">
|
||||
<div
|
||||
class="post-images"
|
||||
v-if="post.images && post.images.length > 0"
|
||||
:class="{ 'is-three': post.images.length >= 3 }"
|
||||
>
|
||||
<div
|
||||
v-for="(img, index) in post.images"
|
||||
:key="index"
|
||||
class="image-item"
|
||||
@click.stop
|
||||
>
|
||||
<el-image
|
||||
:preview-teleported="true"
|
||||
:src="img"
|
||||
fit="cover"
|
||||
:preview-src-list="post.images"
|
||||
:initial-index="index"
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-error">
|
||||
<el-icon>
|
||||
@ -44,12 +70,15 @@
|
||||
</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> -->
|
||||
<button
|
||||
v-if="!post.isPremium"
|
||||
type="button"
|
||||
class="post-pin-btn"
|
||||
:disabled="pinning"
|
||||
@click.stop="handlePinTop"
|
||||
>
|
||||
置顶
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,179 +86,311 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ChatDotRound, Promotion, Picture } from '@element-plus/icons-vue'
|
||||
import { computed, ref } from "vue";
|
||||
import { ChatDotRound, Picture } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { tbGsxtXxltHfid, tbGsxtXxltUpdate } from "@/api/tbGsxtXxltHf.js";
|
||||
|
||||
const props = defineProps({
|
||||
post: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['like', 'click'])
|
||||
const emit = defineEmits(["like", "click", "pin"]);
|
||||
|
||||
const getTagType = (tag) => {
|
||||
const tagMap = {
|
||||
'已动态': 'success',
|
||||
'校长': 'warning',
|
||||
'校本部': 'info'
|
||||
const pinning = ref(false);
|
||||
|
||||
const handlePinTop = async () => {
|
||||
if (pinning.value) return;
|
||||
pinning.value = true;
|
||||
try {
|
||||
const detail = await tbGsxtXxltHfid(props.post.id);
|
||||
await tbGsxtXxltUpdate({ ...detail, sfzd: 1 });
|
||||
ElMessage.success("置顶成功");
|
||||
emit("pin");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
ElMessage.error("置顶失败");
|
||||
} finally {
|
||||
pinning.value = false;
|
||||
}
|
||||
return 'warning'
|
||||
}
|
||||
};
|
||||
|
||||
const handleLike = () => {
|
||||
emit('like', props.post)
|
||||
}
|
||||
const EXCERPT_LEN = 160;
|
||||
|
||||
const rawContent = computed(() => (props.post.content || "").trim());
|
||||
|
||||
const postTitle = computed(() => {
|
||||
const t = rawContent.value;
|
||||
if (!t) return "";
|
||||
const idx = t.indexOf("\n");
|
||||
if (idx === -1) return "";
|
||||
const first = t.slice(0, idx).trim();
|
||||
return first.length > 0 ? first : "";
|
||||
});
|
||||
|
||||
const bodySource = computed(() => {
|
||||
const t = rawContent.value;
|
||||
if (!t) return "";
|
||||
const idx = t.indexOf("\n");
|
||||
if (idx === -1) return t;
|
||||
const rest = t.slice(idx + 1).trim();
|
||||
return rest;
|
||||
});
|
||||
|
||||
const showFullLink = computed(() => bodySource.value.length > EXCERPT_LEN);
|
||||
|
||||
const displayBody = computed(() => {
|
||||
const b = bodySource.value;
|
||||
if (!b) return "";
|
||||
if (b.length <= EXCERPT_LEN) return b;
|
||||
return `${b.slice(0, EXCERPT_LEN)}…`;
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.post)
|
||||
}
|
||||
emit("click", props.post);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/luntan-tech.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;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 18px 18px 14px;
|
||||
margin-bottom: 14px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
@include lt-panel-frame;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12) !important;
|
||||
border-color: lt-blue(0.35);
|
||||
box-shadow: $lt-glow-strong, inset 0 1px 0 lt-blue(0.12);
|
||||
}
|
||||
}
|
||||
|
||||
.post-pin-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 3px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
$lt-bar-blue-start 0%,
|
||||
$lt-bar-blue-end 100%
|
||||
);
|
||||
box-shadow: 0 1px 5px lt-blue(0.2);
|
||||
transition: opacity 0.2s ease, transform 0.15s ease;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
$lt-bar-blue-start 0%,
|
||||
$lt-bar-blue-end 100%
|
||||
);
|
||||
color: #fff;
|
||||
padding: 3px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
box-shadow: 0 2px 10px lt-blue(0.28);
|
||||
}
|
||||
|
||||
.post-main-content {
|
||||
display: flex !important;
|
||||
gap: 12px !important;
|
||||
align-items: flex-start !important;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.post-avatar {
|
||||
flex-shrink: 0 !important;
|
||||
flex-shrink: 0;
|
||||
border: 2px solid lt-blue(0.35);
|
||||
box-shadow: 0 0 10px lt-blue(0.2);
|
||||
}
|
||||
|
||||
.post-right {
|
||||
flex: 1 !important;
|
||||
min-width: 0 !important;
|
||||
padding-top: 2px !important;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.post-header {
|
||||
margin-bottom: 12px !important;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.user-name-row {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 8px !important;
|
||||
margin-bottom: 4px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 16px !important;
|
||||
font-weight: 500 !important;
|
||||
color: #303133 !important;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
}
|
||||
|
||||
.level-badge {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 2px;
|
||||
background: lt-blue(0.1);
|
||||
color: $lt-bar-blue-end;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
border: 1px solid lt-blue(0.28);
|
||||
}
|
||||
|
||||
.post-time {
|
||||
font-size: 12px !important;
|
||||
color: #909399 !important;
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
margin-bottom: 16px !important;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: $lt-text;
|
||||
line-height: 1.45;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.post-text-wrap {
|
||||
font-size: 14px;
|
||||
line-height: 1.65;
|
||||
color: $lt-text-dim;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
font-size: 14px !important;
|
||||
line-height: 1.6 !important;
|
||||
color: #606266 !important;
|
||||
margin-bottom: 12px !important;
|
||||
word-break: break-word !important;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.full-text-link {
|
||||
margin-left: 4px;
|
||||
color: $lt-bar-blue-end;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-shadow: 0 0 8px lt-blue(0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.post-images {
|
||||
display: flex !important;
|
||||
gap: 8px !important;
|
||||
flex-wrap: wrap !important;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 12px;
|
||||
|
||||
&.is-three .image-item {
|
||||
width: calc((100% - 16px) / 3);
|
||||
min-width: 0;
|
||||
aspect-ratio: 4 / 3;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.image-item {
|
||||
width: 200px !important;
|
||||
height: 140px !important;
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
cursor: pointer !important;
|
||||
width: 160px;
|
||||
height: 112px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid $lt-border-dim;
|
||||
box-shadow: 0 0 10px rgba(0, 80, 140, 0.25);
|
||||
|
||||
:deep(.el-image) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.post-images.is-three .image-item {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(10, 30, 60, 0.65);
|
||||
color: $lt-text-muted;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
display: flex !important;
|
||||
gap: 24px !important;
|
||||
padding-top: 12px !important;
|
||||
border-top: 1px solid #f0f0f0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px 20px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: $lt-text-muted;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: #409eff !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #409eff !important;
|
||||
color: $lt-bar-blue-end;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 18px !important;
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.author-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
background: lt-blue(0.08);
|
||||
color: $lt-bar-btn-text;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
font-weight: 600;
|
||||
border: 1px solid lt-blue(0.25);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,186 +1,555 @@
|
||||
<template>
|
||||
<div class="post-list">
|
||||
<!-- 发布按钮 -->
|
||||
<div class="publish-section">
|
||||
<el-button type="primary" @click="showPublishDialog = true">
|
||||
<el-icon>
|
||||
<Edit />
|
||||
</el-icon>
|
||||
发布帖子
|
||||
</el-button>
|
||||
<div class="post-list">
|
||||
<!-- 贴吧风格顶栏:左信息 + 右操作(页面背景仍为外层网格,不变) -->
|
||||
<div class="bar-head">
|
||||
<div class="bar-logo">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
</div>
|
||||
<div class="bar-text-block">
|
||||
<div class="bar-title">信息论坛</div>
|
||||
<div class="bar-meta-row">
|
||||
<span class="bar-meta-item">帖子 {{ replyCountStub.fts }}</span>
|
||||
<span class="bar-meta-sep">·</span>
|
||||
<span class="bar-meta-item bar-meta-reply"
|
||||
>回复 {{ replyCountStub.hfs }}</span
|
||||
>
|
||||
</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>
|
||||
<div class="bar-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="bar-pill bar-pill-primary"
|
||||
@click="showPublishDialog = true"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
发帖
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 帖子列表 -->
|
||||
<div
|
||||
class="posts-container"
|
||||
v-loading="loading"
|
||||
v-infinite-scroll="loadMore"
|
||||
:infinite-scroll-disabled="scrollDisabled"
|
||||
:infinite-scroll-distance="100"
|
||||
>
|
||||
<!-- 置顶:仅标题缩略列表 -->
|
||||
<div v-if="pinnedList.length" class="pinned-block">
|
||||
<div class="pinned-list">
|
||||
<div
|
||||
v-for="post in pinnedList"
|
||||
:key="'zd-' + post.id"
|
||||
class="pinned-row"
|
||||
@click="handlePostClick(post)"
|
||||
>
|
||||
<span class="pinned-badge">置顶</span>
|
||||
<span class="pinned-title">{{ displayTitle(post) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分界线 -->
|
||||
<div v-if="pinnedList.length && normalList.length" class="list-divider">
|
||||
<span class="divider-line" />
|
||||
<span class="divider-text">全部帖子</span>
|
||||
<span class="divider-line" />
|
||||
</div>
|
||||
|
||||
<!-- 普通帖子 -->
|
||||
<PostItem
|
||||
v-for="post in normalList"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
@like="handleLike"
|
||||
@pin="handlePostPinned"
|
||||
@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 && normalList.length > 0" class="no-more">
|
||||
没有更多数据了
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<el-empty
|
||||
v-if="!loading && postList.length === 0"
|
||||
class="post-empty"
|
||||
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)
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { ChatDotRound, Loading, Plus } 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";
|
||||
import { qcckGet } from "@/api/qcckApi.js";
|
||||
const loading = ref(false);
|
||||
const loadingMore = ref(false);
|
||||
const postList = ref([]);
|
||||
const showPublishDialog = ref(false);
|
||||
|
||||
const listQuery = ref({
|
||||
pageCurrent: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
pageCurrent: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
|
||||
const total = ref(0)
|
||||
const total = ref(0);
|
||||
// 获取置顶数据
|
||||
const pinnedList = ref([]);
|
||||
const getpinnedList = () => {
|
||||
const params = {
|
||||
pageCurrent: 1,
|
||||
pageSize: 10,
|
||||
sfzd: 1
|
||||
};
|
||||
tbGsxtXxltSelectPage(params).then((res) => {
|
||||
pinnedList.value = (res.records || []).map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title || "",
|
||||
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
|
||||
}));
|
||||
});
|
||||
};
|
||||
const normalList = computed(() => postList.value);
|
||||
|
||||
/** 置顶行展示标题:优先接口 title,否则从正文首行截取 */
|
||||
function displayTitle(post) {
|
||||
const tit = (post.title || "").trim();
|
||||
if (tit) {
|
||||
return tit.length <= 56 ? tit : `${tit.slice(0, 56)}…`;
|
||||
}
|
||||
const t = (post.content || "").trim();
|
||||
if (!t) return "无标题";
|
||||
const idx = t.indexOf("\n");
|
||||
const firstLine = idx === -1 ? t : t.slice(0, idx).trim();
|
||||
const line = firstLine || t.slice(0, 80);
|
||||
if (line.length <= 56) return line;
|
||||
return `${line.slice(0, 56)}…`;
|
||||
}
|
||||
|
||||
// 计算是否禁用滚动加载
|
||||
const scrollDisabled = computed(() => {
|
||||
return loadingMore.value || noMore.value
|
||||
})
|
||||
return loadingMore.value || noMore.value;
|
||||
});
|
||||
|
||||
// 计算是否没有更多数据
|
||||
const noMore = computed(() => {
|
||||
return postList.value.length >= total.value && total.value > 0
|
||||
})
|
||||
return postList.value.length >= total.value && total.value > 0;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadPosts()
|
||||
})
|
||||
getpinnedList();
|
||||
loadPosts();
|
||||
getStatistics();
|
||||
});
|
||||
|
||||
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,
|
||||
title: item.title || "",
|
||||
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) {
|
||||
loadingMore.value = true
|
||||
postList.value = [...postList.value, ...data];
|
||||
} else {
|
||||
loading.value = true
|
||||
postList.value = data;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
if (postList.value.length >= total.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
listQuery.value.pageCurrent++
|
||||
loadPosts(true)
|
||||
}
|
||||
listQuery.value.pageCurrent++;
|
||||
loadPosts(true);
|
||||
};
|
||||
|
||||
const handleLike = (post) => {
|
||||
post.isLiked = !post.isLiked
|
||||
post.likeCount += post.isLiked ? 1 : -1
|
||||
ElMessage.success(post.isLiked ? '点赞成功' : '取消点赞')
|
||||
}
|
||||
post.isLiked = !post.isLiked;
|
||||
post.likeCount += post.isLiked ? 1 : -1;
|
||||
ElMessage.success(post.isLiked ? "点赞成功" : "取消点赞");
|
||||
};
|
||||
|
||||
const handlePublishSuccess = () => {
|
||||
// 重置分页并重新加载
|
||||
listQuery.value.pageCurrent = 1
|
||||
loadPosts()
|
||||
}
|
||||
// 重置分页并重新加载
|
||||
listQuery.value.pageCurrent = 1;
|
||||
loadPosts();
|
||||
getStatistics();
|
||||
};
|
||||
|
||||
const emit = defineEmits(['openDetail'])
|
||||
const handlePostPinned = () => {
|
||||
listQuery.value.pageCurrent = 1;
|
||||
loadPosts(false);
|
||||
getpinnedList();
|
||||
getStatistics();
|
||||
};
|
||||
const replyCountStub = ref({
|
||||
fts: 0,
|
||||
hfs: 0
|
||||
});
|
||||
const totalDisplay = (n) => {
|
||||
if (n >= 10000) return `${(n / 10000).toFixed(1)}W`;
|
||||
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
|
||||
return String(n);
|
||||
};
|
||||
// 获取统计数量
|
||||
const getStatistics = () => {
|
||||
qcckGet({}, "/mosty-gsxt/tbGsxtXxlt/lttj").then((res) => {
|
||||
console.log(res);
|
||||
replyCountStub.value = {
|
||||
fts: totalDisplay(res.fts),
|
||||
hfs: totalDisplay(res.hfs)
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const emit = defineEmits(["openDetail"]);
|
||||
|
||||
const handlePostClick = (post) => {
|
||||
emit('openDetail', post)
|
||||
}
|
||||
emit("openDetail", post);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.post-list {
|
||||
background: transparent;
|
||||
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);
|
||||
.bar-head {
|
||||
--bar-logo-size: 72px;
|
||||
--bar-logo-gap: 18px;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
grid-template-rows: auto;
|
||||
column-gap: var(--bar-logo-gap);
|
||||
align-items: center;
|
||||
margin-bottom: 18px;
|
||||
padding: 22px 22px 20px;
|
||||
border-radius: 12px;
|
||||
/* 深蓝科技横幅:左亮右暗,贴近深色企业顶栏 */
|
||||
background-color: #0a0f18;
|
||||
background-image: linear-gradient(
|
||||
92deg,
|
||||
rgba(147, 197, 253, 0.09) 0%,
|
||||
rgba(96, 165, 250, 0.04) 6%,
|
||||
transparent 14%
|
||||
),
|
||||
linear-gradient(100deg, rgba(255, 255, 255, 0.05) 0%, transparent 12%),
|
||||
linear-gradient(
|
||||
110deg,
|
||||
#2a4f8c 0%,
|
||||
#1a3358 26%,
|
||||
#121c2e 58%,
|
||||
#101828 78%,
|
||||
#080d14 100%
|
||||
);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.38),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
|
||||
.bar-pill {
|
||||
color: $lt-bar-btn-text;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.95);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background: #f8fafc;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar-logo {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
align-self: center;
|
||||
flex-shrink: 0;
|
||||
width: var(--bar-logo-size);
|
||||
height: var(--bar-logo-size);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
|
||||
|
||||
.el-icon {
|
||||
font-size: 38px;
|
||||
color: $lt-cyan-mid;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-text-block {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
align-self: center;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bar-title {
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
color: #ffffff;
|
||||
letter-spacing: 0.04em;
|
||||
line-height: 1.2;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.bar-meta-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 10px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
line-height: 1.4;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.bar-meta-sep {
|
||||
opacity: 0.65;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bar-meta-reply {
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 12px rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.bar-actions {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.bar-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 10px 22px;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
border: 1px solid $lt-cyan-mid;
|
||||
background: $lt-cyan-mid;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.85;
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
.bar-pill-primary {
|
||||
&:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
.bar-head .bar-pill-primary:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// 置顶缩略区
|
||||
.pinned-block {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.pinned-list {
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
background: $lt-panel-soft;
|
||||
border: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
.pinned-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px 4px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background 0.15s ease;
|
||||
|
||||
& + .pinned-row {
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(64, 158, 255, 0.06);
|
||||
}
|
||||
}
|
||||
|
||||
.pinned-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 2px 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: $lt-cyan-mid;
|
||||
border: 1px solid rgba(64, 158, 255, 0.4);
|
||||
border-radius: 4px;
|
||||
background: rgba(64, 158, 255, 0.06);
|
||||
}
|
||||
|
||||
.pinned-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $lt-text;
|
||||
line-height: 1.45;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
// 置顶与普通帖之间的分界
|
||||
.list-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 16px 0 14px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: $lt-border-dim;
|
||||
}
|
||||
|
||||
.divider-text {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: $lt-text-muted;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.posts-container {
|
||||
min-height: 400px;
|
||||
min-height: 400px;
|
||||
|
||||
:deep(.el-loading-mask) {
|
||||
background-color: rgba(245, 247, 250, 0.7);
|
||||
}
|
||||
|
||||
:deep(.el-loading-spinner .path) {
|
||||
stroke: $lt-cyan-mid;
|
||||
}
|
||||
}
|
||||
|
||||
.post-empty {
|
||||
padding: 48px 0;
|
||||
|
||||
:deep(.el-empty__description) {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
color: $lt-text-muted;
|
||||
font-size: 14px;
|
||||
gap: 8px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
color: $lt-cyan-mid;
|
||||
}
|
||||
}
|
||||
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #c0c4cc;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: $lt-text-muted;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,147 +1,172 @@
|
||||
<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-dialog
|
||||
v-model="dialogVisible"
|
||||
class="luntan-tech-dialog"
|
||||
title="发布帖子"
|
||||
width="60%"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<div style="overflow: auto; height: 60vh">
|
||||
<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="内容" 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="表情">
|
||||
<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>
|
||||
<el-form-item label="图片">
|
||||
<Upload v-model="imageIds" :limit="9" :isImg="true" :isAll="true" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitting">
|
||||
发布
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<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'
|
||||
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: '活动'
|
||||
}
|
||||
|
||||
"Smileys & Emotion": "笑脸&表情",
|
||||
"Food & Drink": "食物&饮料",
|
||||
"Animals & Nature": "动物&自然",
|
||||
"Travel & Places": "旅行&地点",
|
||||
"People & Body": "人物&身体",
|
||||
Objects: "物品",
|
||||
Symbols: "符号",
|
||||
Flags: "旗帜",
|
||||
Activities: "活动"
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
const emit = defineEmits(["update:modelValue", "success"]);
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const formRef = ref()
|
||||
const submitting = ref(false)
|
||||
const imageIds = ref([])
|
||||
const dialogVisible = ref(false);
|
||||
const formRef = ref();
|
||||
const submitting = ref(false);
|
||||
const imageIds = ref([]);
|
||||
|
||||
const form = ref({
|
||||
title: '',
|
||||
content: ''
|
||||
})
|
||||
title: "",
|
||||
content: ""
|
||||
});
|
||||
|
||||
const rules = {
|
||||
title: [
|
||||
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: '请输入内容', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
title: [{ required: true, message: "请输入标题", trigger: "blur" }],
|
||||
content: [{ required: true, message: "请输入内容", trigger: "blur" }]
|
||||
};
|
||||
|
||||
watch(() => props.modelValue, (val) => {
|
||||
dialogVisible.value = val
|
||||
})
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
dialogVisible.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
watch(dialogVisible, (val) => {
|
||||
emit('update:modelValue', val)
|
||||
if (!val) {
|
||||
resetForm()
|
||||
}
|
||||
})
|
||||
emit("update:modelValue", val);
|
||||
if (!val) {
|
||||
resetForm();
|
||||
}
|
||||
});
|
||||
|
||||
const onEmojiClick = (emoji) => {
|
||||
form.value.content += emoji
|
||||
}
|
||||
form.value.content += emoji;
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
if (!formRef.value) return;
|
||||
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
submitting.value = true
|
||||
try {
|
||||
const ltmasg = getItem('ltmasg')
|
||||
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 || ''
|
||||
}
|
||||
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)
|
||||
await tbGsxtXxltSave(postData);
|
||||
|
||||
ElMessage.success('发布成功')
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
console.error('发布失败', error)
|
||||
ElMessage.error('发布失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
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()
|
||||
}
|
||||
form.value = {
|
||||
title: "",
|
||||
content: ""
|
||||
};
|
||||
imageIds.value = [];
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 样式已由Upload组件内部处理</style>
|
||||
// Upload 等子组件样式在各自内部
|
||||
::v-deep .form-item-box {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../styles/luntan-dialog-tech.scss";
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
class="luntan-tech-dialog"
|
||||
title="发表回复"
|
||||
width="600px"
|
||||
:before-close="handleClose"
|
||||
@ -155,3 +156,7 @@ const resetForm = () => {
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/luntan-dialog-tech.scss';
|
||||
</style>
|
||||
|
||||
@ -1,49 +1,78 @@
|
||||
<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 class="user-card-head">
|
||||
<div class="user-avatar">
|
||||
<div class="avatar-wrapper" @click="showAvatarDialog = true">
|
||||
<el-avatar :size="56" :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-card-head-text">
|
||||
<div class="name-row">
|
||||
<span class="nickname">{{ userInfo.nickname || "用户信息" }}</span>
|
||||
</div>
|
||||
<div class="sub-stats">内部论坛 · 已登录</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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span class="label">部门</span>
|
||||
<span class="value">{{ userInfo.department || "-" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 更换头像对话框 -->
|
||||
<ChangeAvatar v-model="showAvatarDialog" title="更换头像" @avatarUpdated="handleAvatarUpdated" />
|
||||
<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-dialog
|
||||
v-model="showNicknameDialog"
|
||||
class="luntan-tech-dialog"
|
||||
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-input
|
||||
v-model="nicknameForm.nickname"
|
||||
placeholder="请输入昵称"
|
||||
maxlength="20"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -54,168 +83,186 @@
|
||||
</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'
|
||||
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 showAvatarDialog = ref(false);
|
||||
const showNicknameDialog = ref(false);
|
||||
const nicknameFormRef = ref();
|
||||
|
||||
const userInfo = ref({
|
||||
avatar: '',
|
||||
account: '',
|
||||
name: '',
|
||||
department: '',
|
||||
nickname: ''
|
||||
})
|
||||
avatar: "",
|
||||
account: "",
|
||||
name: "",
|
||||
department: "",
|
||||
nickname: ""
|
||||
});
|
||||
|
||||
const nicknameForm = reactive({
|
||||
nickname: ''
|
||||
})
|
||||
nickname: ""
|
||||
});
|
||||
|
||||
const nicknameRules = {
|
||||
nickname: [
|
||||
{ required: true, message: '请输入昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '昵称长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
{ 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) : ''
|
||||
})
|
||||
return userInfo.value.avatar ? setAddress(userInfo.value.avatar) : "";
|
||||
});
|
||||
|
||||
// 加载用户信息
|
||||
const loadUserInfo = async () => {
|
||||
const sfzh = getItem('idEntityCard')
|
||||
let ltmasg = getItem('ltmasg')
|
||||
const sfzh = getItem("idEntityCard");
|
||||
let ltmasg = getItem("ltmasg");
|
||||
|
||||
if (!ltmasg) {
|
||||
try {
|
||||
const res = await tbGsxtXxltTxTxQueryBySfzh({ sfzh: sfzh })
|
||||
const res = await tbGsxtXxltTxTxQueryBySfzh({ sfzh: sfzh });
|
||||
console.log(res);
|
||||
|
||||
const deptId = getItem('deptId')?.[0]
|
||||
const deptId = getItem("deptId")?.[0];
|
||||
ltmasg = {
|
||||
...res,
|
||||
deptName: deptId?.deptName || ''
|
||||
}
|
||||
setItem('ltmasg', ltmasg)
|
||||
deptName: deptId?.deptName || ""
|
||||
};
|
||||
setItem("ltmasg", ltmasg);
|
||||
} catch (error) {
|
||||
console.error('加载用户信息失败:', 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 || ''
|
||||
}
|
||||
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 ltmasg = getItem("ltmasg");
|
||||
const updateData = {
|
||||
...ltmasg,
|
||||
tx: newAvatar
|
||||
}
|
||||
};
|
||||
|
||||
await tbGsxtXxltTxTxSave(updateData)
|
||||
removeItem('ltmasg')
|
||||
await loadUserInfo()
|
||||
ElMessage.success('头像更新成功')
|
||||
await tbGsxtXxltTxTxSave(updateData);
|
||||
removeItem("ltmasg");
|
||||
await loadUserInfo();
|
||||
ElMessage.success("头像更新成功");
|
||||
} catch (error) {
|
||||
console.error('更新头像失败:', error)
|
||||
ElMessage.error('头像更新失败,请重试')
|
||||
console.error("更新头像失败:", error);
|
||||
ElMessage.error("头像更新失败,请重试");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 处理保存昵称
|
||||
const handleSaveNickname = async () => {
|
||||
if (!nicknameFormRef.value) return
|
||||
if (!nicknameFormRef.value) return;
|
||||
|
||||
await nicknameFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
const ltmasg = getItem('ltmasg')
|
||||
const ltmasg = getItem("ltmasg");
|
||||
const updateData = {
|
||||
...ltmasg,
|
||||
nc: nicknameForm.nickname
|
||||
}
|
||||
};
|
||||
|
||||
await tbGsxtXxltTxTxSave(updateData)
|
||||
removeItem('ltmasg')
|
||||
await loadUserInfo()
|
||||
showNicknameDialog.value = false
|
||||
ElMessage.success('昵称保存成功')
|
||||
await tbGsxtXxltTxTxSave(updateData);
|
||||
removeItem("ltmasg");
|
||||
await loadUserInfo();
|
||||
showNicknameDialog.value = false;
|
||||
ElMessage.success("昵称保存成功");
|
||||
} catch (error) {
|
||||
console.error('保存昵称失败:', error)
|
||||
ElMessage.error('昵称保存失败,请重试')
|
||||
console.error("保存昵称失败:", error);
|
||||
ElMessage.error("昵称保存失败,请重试");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 监听昵称对话框打开,初始化表单
|
||||
const openNicknameDialog = () => {
|
||||
nicknameForm.nickname = userInfo.value.nickname
|
||||
}
|
||||
nicknameForm.nickname = userInfo.value.nickname;
|
||||
};
|
||||
|
||||
// 监听对话框显示状态
|
||||
const unwatchNickname = () => {
|
||||
if (showNicknameDialog.value) {
|
||||
openNicknameDialog()
|
||||
openNicknameDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadUserInfo()
|
||||
})
|
||||
loadUserInfo();
|
||||
});
|
||||
|
||||
// 监听昵称对话框
|
||||
const stopWatch = () => {
|
||||
if (showNicknameDialog.value) {
|
||||
nicknameForm.nickname = userInfo.value.nickname
|
||||
nicknameForm.nickname = userInfo.value.nickname;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 使用 watch 监听对话框状态
|
||||
import { watch } from 'vue'
|
||||
import { watch } from "vue";
|
||||
watch(showNicknameDialog, (newVal) => {
|
||||
if (newVal) {
|
||||
nicknameForm.nickname = userInfo.value.nickname
|
||||
nicknameForm.nickname = userInfo.value.nickname;
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../styles/luntan-tech.scss";
|
||||
|
||||
.user-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
padding: 18px 16px 16px;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
$lt-bar-blue-start 0%,
|
||||
$lt-bar-blue-end 100%
|
||||
);
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
box-shadow: 0 4px 18px lt-blue(0.22);
|
||||
}
|
||||
|
||||
.user-card-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||
|
||||
&:hover .avatar-overlay {
|
||||
opacity: 1;
|
||||
@ -227,7 +274,7 @@ watch(showNicknameDialog, (newVal) => {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -236,19 +283,41 @@ watch(showNicknameDialog, (newVal) => {
|
||||
border-radius: 50%;
|
||||
|
||||
.upload-icon {
|
||||
font-size: 24px;
|
||||
font-size: 22px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card-head-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.name-row {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.sub-stats {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
@ -257,14 +326,14 @@ watch(showNicknameDialog, (newVal) => {
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
padding: 6px 8px;
|
||||
margin-left: -8px;
|
||||
margin-right: -8px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s ease;
|
||||
border-radius: 6px;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
|
||||
.edit-icon {
|
||||
opacity: 1;
|
||||
@ -273,23 +342,28 @@ watch(showNicknameDialog, (newVal) => {
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #909399;
|
||||
min-width: 50px;
|
||||
color: rgba(255, 255, 255, 0.72);
|
||||
min-width: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #303133;
|
||||
color: #ffffff;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
margin-left: 8px;
|
||||
color: #409eff;
|
||||
margin-left: 6px;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
font-size: 14px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../styles/luntan-dialog-tech.scss";
|
||||
</style>
|
||||
|
||||
@ -1,82 +1,304 @@
|
||||
<template>
|
||||
<div class="luntan-container">
|
||||
<!-- 列表页 -->
|
||||
<!-- 列表页:主列帖子 + 右侧信息栏 -->
|
||||
<template v-if="!showDetail">
|
||||
<div class="luntan-left">
|
||||
<UserCard />
|
||||
</div>
|
||||
<div class="luntan-center">
|
||||
<div class="luntan-main">
|
||||
<PostList @openDetail="handleOpenDetail" />
|
||||
</div>
|
||||
<div class="luntan-right">
|
||||
<!-- 右侧留白区域,可以后续添加其他内容 -->
|
||||
</div>
|
||||
<aside class="luntan-sidebar">
|
||||
<UserCard />
|
||||
<div class="hot-news-card" v-loading="hotLoading">
|
||||
<div class="hot-news-head">
|
||||
<span class="hot-news-title">热度消息</span>
|
||||
<span class="hot-news-badge">HOT</span>
|
||||
</div>
|
||||
<ul class="hot-news-list">
|
||||
<li
|
||||
v-for="(item, index) in hotList"
|
||||
:key="item.id"
|
||||
class="hot-news-item"
|
||||
@click="handleOpenDetail(item)"
|
||||
>
|
||||
<span class="hot-rank" :class="{ 'is-top': index < 3 }">{{
|
||||
index + 1
|
||||
}}</span>
|
||||
<span class="hot-item-title">{{ item.lineTitle }}</span>
|
||||
</li>
|
||||
<li
|
||||
v-if="!hotLoading && hotList.length === 0"
|
||||
class="hot-news-empty"
|
||||
>
|
||||
暂无热度内容
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<!-- 详情页 -->
|
||||
<template v-else>
|
||||
<div class="luntan-left"></div>
|
||||
<div class="luntan-detail">
|
||||
<PostDetail :post-id="currentPostId" @back="handleBack" />
|
||||
<div class="luntan-detail-wrap">
|
||||
<div class="luntan-detail">
|
||||
<PostDetail :post-id="currentPostId" @back="handleBack" />
|
||||
</div>
|
||||
</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'
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import UserCard from "./components/UserCard.vue";
|
||||
import PostList from "./components/PostList.vue";
|
||||
import PostDetail from "./components/PostDetail.vue";
|
||||
import { tbGsxtXxltSelectPage } from "@/api/tbGsxtXxltHf";
|
||||
import { setAddress } from "@/utils/tools";
|
||||
import { qcckGet } from "@/api/qcckApi.js";
|
||||
const showDetail = ref(false);
|
||||
const currentPostId = ref(null);
|
||||
|
||||
const showDetail = ref(false)
|
||||
const currentPostId = ref(null)
|
||||
const hotList = ref([]);
|
||||
const hotLoading = ref(false);
|
||||
|
||||
function lineTitleFromRecord(item) {
|
||||
const tit = (item.title || "").trim();
|
||||
if (tit) {
|
||||
return tit.length > 40 ? `${tit.slice(0, 40)}…` : tit;
|
||||
}
|
||||
const t = (item.content || "").trim();
|
||||
if (!t) return "无标题";
|
||||
const idx = t.indexOf("\n");
|
||||
const first = idx === -1 ? t : t.slice(0, idx).trim();
|
||||
const line = first || t.slice(0, 60);
|
||||
return line.length > 40 ? `${line.slice(0, 40)}…` : line;
|
||||
}
|
||||
|
||||
function mapHotPost(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
lineTitle: lineTitleFromRecord(item),
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
const loadHotNews = () => {
|
||||
hotLoading.value = true;
|
||||
qcckGet({}, "/mosty-gsxt/tbGsxtXxlt/hotList")
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
hotList.value = res.map(mapHotPost);
|
||||
hotLoading.value = false;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
hotLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadHotNews();
|
||||
});
|
||||
|
||||
const handleOpenDetail = (post) => {
|
||||
currentPostId.value = post.id
|
||||
showDetail.value = true
|
||||
}
|
||||
currentPostId.value = post.id;
|
||||
showDetail.value = true;
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
showDetail.value = false
|
||||
currentPostId.value = null
|
||||
}
|
||||
showDetail.value = false;
|
||||
currentPostId.value = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/luntan-tech.scss";
|
||||
|
||||
.luntan-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: calc(100vh - 60px);
|
||||
gap: 24px;
|
||||
padding: 20px 24px;
|
||||
box-sizing: border-box;
|
||||
/* 固定可视高度 + 隐藏溢出,让主列在内部滚动(flex 子项需 min-height:0) */
|
||||
height: calc(100vh - 60px);
|
||||
max-height: calc(100vh - 60px);
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.luntan-left {
|
||||
width: 240px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.luntan-center {
|
||||
.luntan-main {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.luntan-right {
|
||||
width: 240px;
|
||||
.luntan-sidebar {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 400px;
|
||||
flex-shrink: 0;
|
||||
min-height: 0;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hot-news-card {
|
||||
position: relative;
|
||||
flex: 0 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.el-loading-mask) {
|
||||
background-color: rgba(245, 247, 250, 0.7);
|
||||
}
|
||||
|
||||
:deep(.el-loading-spinner .path) {
|
||||
stroke: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.hot-news-head {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 16px 18px 14px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.hot-news-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.hot-news-badge {
|
||||
flex-shrink: 0;
|
||||
padding: 3px 10px;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.12em;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(180deg, #ff7a3c 0%, #ff5722 100%);
|
||||
}
|
||||
|
||||
.hot-news-list {
|
||||
margin: 0;
|
||||
padding: 8px 12px 12px;
|
||||
list-style: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.hot-news-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 12px 8px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease;
|
||||
|
||||
& + .hot-news-item {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(64, 158, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.hot-rank {
|
||||
flex-shrink: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #4b5563;
|
||||
border-radius: 4px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
|
||||
&.is-top {
|
||||
color: #fff;
|
||||
background: linear-gradient(180deg, #ff7a3c 0%, #ff5722 100%);
|
||||
border-color: rgba(255, 87, 34, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.hot-item-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
color: $lt-text;
|
||||
font-weight: 500;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.hot-news-item:hover .hot-item-title {
|
||||
color: $lt-bar-blue-end;
|
||||
}
|
||||
|
||||
.hot-news-empty {
|
||||
padding: 28px 12px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: $lt-text-muted;
|
||||
list-style: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.luntan-detail-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.luntan-detail {
|
||||
height: 83vh;
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
border-radius: 4px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 非 scoped:el-image-viewer 通过 Teleport 挂到 body,需全局选择器 -->
|
||||
<style lang="scss">
|
||||
@import "./styles/luntan-tech.scss";
|
||||
</style>
|
||||
|
||||
104
src/views/backOfficeSystem/luntan/styles/luntan-dialog-tech.scss
Normal file
104
src/views/backOfficeSystem/luntan/styles/luntan-dialog-tech.scss
Normal file
@ -0,0 +1,104 @@
|
||||
@import "./luntan-tech.scss";
|
||||
@import "./luntan-v3emoji-tech.scss";
|
||||
|
||||
.luntan-tech-dialog.el-dialog {
|
||||
background: rgba(8, 20, 48, 0.98) !important;
|
||||
border: 1px solid $lt-border;
|
||||
border-radius: 4px;
|
||||
box-shadow: $lt-glow-strong, 0 16px 48px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__header {
|
||||
border-bottom: 1px solid $lt-border-dim;
|
||||
padding: 14px 18px;
|
||||
margin: 0;
|
||||
background: rgba(8, 20, 48, 0.98) !important;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__title {
|
||||
color: $lt-text;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__headerbtn .el-dialog__close {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__headerbtn:hover .el-dialog__close {
|
||||
color: $lt-cyan;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__body {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-dialog__footer {
|
||||
border-top: 1px solid $lt-border-dim;
|
||||
padding: 12px 18px;
|
||||
background: rgba(8, 20, 48, 0.98) !important;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-form-item__label {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-input__wrapper {
|
||||
background: rgba(10, 28, 58, 0.85) !important;
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.28) inset !important;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.4) inset !important;
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
box-shadow: 0 0 0 1px rgba(0, 227, 255, 0.55) inset,
|
||||
0 0 14px rgba(0, 163, 255, 0.22) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-input__inner {
|
||||
color: $lt-text-dim;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-input__count,
|
||||
.luntan-tech-dialog .el-input__count .el-input__count-inner {
|
||||
background: transparent !important;
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-textarea__inner {
|
||||
background: rgba(10, 28, 58, 0.9) !important;
|
||||
color: $lt-text-dim;
|
||||
border: 1px solid rgba(0, 227, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 0 20px rgba(20, 80, 140, 0.2);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-button--default {
|
||||
background: rgba(15, 40, 75, 0.65);
|
||||
border-color: rgba(0, 163, 255, 0.35);
|
||||
color: $lt-text-dim;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-button--primary {
|
||||
background: linear-gradient(180deg, #00a3ff 0%, #0066bb 100%);
|
||||
border-color: rgba(0, 227, 255, 0.5);
|
||||
box-shadow: 0 0 16px rgba(0, 163, 255, 0.4);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog .el-upload--picture-card {
|
||||
background: rgba(10, 28, 58, 0.75) !important;
|
||||
border-color: rgba(0, 227, 255, 0.35) !important;
|
||||
|
||||
.el-icon {
|
||||
color: $lt-cyan;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(0, 227, 255, 0.55) !important;
|
||||
box-shadow: 0 0 14px rgba(0, 163, 255, 0.25);
|
||||
}
|
||||
}
|
||||
115
src/views/backOfficeSystem/luntan/styles/luntan-tech.scss
Normal file
115
src/views/backOfficeSystem/luntan/styles/luntan-tech.scss
Normal file
@ -0,0 +1,115 @@
|
||||
// 论坛模块 — 白色清新主题变量
|
||||
$lt-bg: #f5f7fa;
|
||||
$lt-bg-soft: #ffffff;
|
||||
$lt-cyan: #4a9eff;
|
||||
$lt-cyan-mid: #409eff;
|
||||
$lt-panel: #ffffff;
|
||||
$lt-panel-soft: #f8fafc;
|
||||
$lt-border: rgba(0, 0, 0, 0.08);
|
||||
$lt-border-dim: rgba(0, 0, 0, 0.04);
|
||||
$lt-text: #333333;
|
||||
$lt-text-dim: #666666;
|
||||
$lt-text-muted: #999999;
|
||||
$lt-glow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||
$lt-glow-strong: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||||
// 顶栏:与浅色页面、白字/白按钮协调的蓝色
|
||||
$lt-bar-blue-start: #3d7dd6;
|
||||
$lt-bar-blue-end: #2563eb;
|
||||
$lt-bar-btn-text: #1d4ed8;
|
||||
|
||||
// 主题蓝 rgba(与顶栏渐变一致,便于详情页等复用)
|
||||
@function lt-blue($alpha) {
|
||||
@return rgba(37, 99, 235, $alpha);
|
||||
}
|
||||
|
||||
@function lt-blue-light($alpha) {
|
||||
@return rgba(61, 125, 214, $alpha);
|
||||
}
|
||||
|
||||
@mixin lt-panel-frame {
|
||||
background: $lt-panel;
|
||||
border: 1px solid $lt-border;
|
||||
box-shadow: $lt-glow;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@mixin lt-panel-soft-bg {
|
||||
background: $lt-panel-soft;
|
||||
border: 1px solid $lt-border-dim;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@mixin lt-corner-brackets {
|
||||
position: relative;
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-color: $lt-cyan-mid;
|
||||
border-style: solid;
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
&::before {
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
border-width: 2px 0 0 2px;
|
||||
box-shadow: -1px -1px 6px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
&::after {
|
||||
bottom: -1px;
|
||||
right: -1px;
|
||||
border-width: 0 2px 2px 0;
|
||||
box-shadow: 1px 1px 6px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
// el-image-viewer 全屏预览覆盖
|
||||
.el-image-viewer__wrapper {
|
||||
background: rgba(0, 0, 0, 0.85) !important;
|
||||
|
||||
.el-image-viewer__mask {
|
||||
background: rgba(0, 0, 0, 0.6) !important;
|
||||
}
|
||||
|
||||
.el-image-viewer__btn {
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
background: rgba(80, 80, 80, 0.7);
|
||||
|
||||
&:hover {
|
||||
background: rgba(100, 100, 100, 0.85);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
&.el-image-viewer__close {
|
||||
top: 24px;
|
||||
right: 28px;
|
||||
}
|
||||
|
||||
&.el-image-viewer__prev {
|
||||
left: 24px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&.el-image-viewer__next {
|
||||
right: 24px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.el-image-viewer__canvas {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
@import './luntan-tech.scss';
|
||||
|
||||
// vue3-emoji(dist/style.css)默认浅色/白底,与论坛科技风统一
|
||||
@mixin lt-v3-emoji-vars {
|
||||
--V3Emoji-backgroundColor: rgba(10, 28, 58, 0.96);
|
||||
--V3Emoji-hoverColor: rgba(0, 100, 160, 0.22);
|
||||
--V3Emoji-activeColor: rgba(0, 130, 200, 0.3);
|
||||
--V3Emoji-fontColor: #{$lt-text};
|
||||
--V3Emoji-borderColor: #{rgba(0, 227, 255, 0.38)};
|
||||
--V3Emoji-borderFocusColor: #{$lt-cyan};
|
||||
--V3Emoji-shadowColor: rgba(0, 40, 90, 0.45);
|
||||
}
|
||||
|
||||
.luntan-tech-dialog,
|
||||
.comment-list .reply-input-box,
|
||||
.luntan-tech-dialog .emoji-row {
|
||||
@include lt-v3-emoji-vars;
|
||||
}
|
||||
|
||||
// 组件内未使用 CSS 变量的原生 input / textarea(仍为白底)
|
||||
.luntan-tech-dialog [class*='emojiInput'] input,
|
||||
.comment-list .reply-input-box [class*='emojiInput'] input {
|
||||
background: rgba(10, 28, 58, 0.92) !important;
|
||||
color: $lt-text-dim !important;
|
||||
border-color: rgba(0, 227, 255, 0.35) !important;
|
||||
box-shadow: inset 0 0 12px rgba(0, 50, 100, 0.25) !important;
|
||||
|
||||
&::placeholder {
|
||||
color: $lt-text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.luntan-tech-dialog [class*='emojiTextarea'] textarea,
|
||||
.comment-list .reply-input-box [class*='emojiTextarea'] textarea {
|
||||
background: rgba(10, 28, 58, 0.92) !important;
|
||||
color: $lt-text-dim !important;
|
||||
border-color: rgba(0, 227, 255, 0.35) !important;
|
||||
box-shadow: inset 0 0 12px rgba(0, 50, 100, 0.25) !important;
|
||||
}
|
||||
|
||||
.luntan-tech-dialog [class*='emojiContainerOpenBtn'],
|
||||
.luntan-tech-dialog [class*='emojiTextareaOpenBtn'],
|
||||
.comment-list .reply-input-box [class*='emojiContainerOpenBtn'],
|
||||
.comment-list .reply-input-box [class*='emojiTextareaOpenBtn'] {
|
||||
color: $lt-cyan !important;
|
||||
filter: drop-shadow(0 0 6px rgba(0, 227, 255, 0.35));
|
||||
}
|
||||
|
||||
// 表情面板可能 teleport / 挂到 body,需单独写选择器
|
||||
[class*='V3Emoji-vue'][class*='__pollup___'],
|
||||
[class*='PollUp-vue'][class*='__pollup___'] {
|
||||
@include lt-v3-emoji-vars;
|
||||
}
|
||||
|
||||
[id='EmojiItem'],
|
||||
[id*='EmojiItem'] {
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 163, 255, 0.45) !important;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(8, 20, 48, 0.88) !important;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user