This commit is contained in:
parent
0ea10e1b23
commit
62359b7fb2
|
|
@ -0,0 +1,60 @@
|
|||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
function authPermission(permission) {
|
||||
const all_permission = "*:*:*";
|
||||
const permissions = useUserStore().permissions
|
||||
if (permission && permission.length > 0) {
|
||||
return permissions.some(v => {
|
||||
return all_permission === v || v === permission
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function authRole(role) {
|
||||
const super_admin = "admin";
|
||||
const roles = useUserStore().roles
|
||||
if (role && role.length > 0) {
|
||||
return roles.some(v => {
|
||||
return super_admin === v || v === role
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
// 验证用户是否具备某权限
|
||||
hasPermi(permission) {
|
||||
return authPermission(permission);
|
||||
},
|
||||
// 验证用户是否含有指定权限,只需包含其中一个
|
||||
hasPermiOr(permissions) {
|
||||
return permissions.some(item => {
|
||||
return authPermission(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否含有指定权限,必须全部拥有
|
||||
hasPermiAnd(permissions) {
|
||||
return permissions.every(item => {
|
||||
return authPermission(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否具备某角色
|
||||
hasRole(role) {
|
||||
return authRole(role);
|
||||
},
|
||||
// 验证用户是否含有指定角色,只需包含其中一个
|
||||
hasRoleOr(roles) {
|
||||
return roles.some(item => {
|
||||
return authRole(item)
|
||||
})
|
||||
},
|
||||
// 验证用户是否含有指定角色,必须全部拥有
|
||||
hasRoleAnd(roles) {
|
||||
return roles.every(item => {
|
||||
return authRole(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
const sessionCache = {
|
||||
set (key, value) {
|
||||
if (!sessionStorage) {
|
||||
return
|
||||
}
|
||||
if (key != null && value != null) {
|
||||
sessionStorage.setItem(key, value)
|
||||
}
|
||||
},
|
||||
get (key) {
|
||||
if (!sessionStorage) {
|
||||
return null
|
||||
}
|
||||
if (key == null) {
|
||||
return null
|
||||
}
|
||||
return sessionStorage.getItem(key)
|
||||
},
|
||||
setJSON (key, jsonValue) {
|
||||
if (jsonValue != null) {
|
||||
this.set(key, JSON.stringify(jsonValue))
|
||||
}
|
||||
},
|
||||
getJSON (key) {
|
||||
const value = this.get(key)
|
||||
if (value != null) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
return null
|
||||
},
|
||||
remove (key) {
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
const localCache = {
|
||||
set (key, value) {
|
||||
if (!localStorage) {
|
||||
return
|
||||
}
|
||||
if (key != null && value != null) {
|
||||
localStorage.setItem(key, value)
|
||||
}
|
||||
},
|
||||
get (key) {
|
||||
if (!localStorage) {
|
||||
return null
|
||||
}
|
||||
if (key == null) {
|
||||
return null
|
||||
}
|
||||
return localStorage.getItem(key)
|
||||
},
|
||||
setJSON (key, jsonValue) {
|
||||
if (jsonValue != null) {
|
||||
this.set(key, JSON.stringify(jsonValue))
|
||||
}
|
||||
},
|
||||
getJSON (key) {
|
||||
const value = this.get(key)
|
||||
if (value != null) {
|
||||
return JSON.parse(value)
|
||||
}
|
||||
return null
|
||||
},
|
||||
remove (key) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 会话级缓存
|
||||
*/
|
||||
session: sessionCache,
|
||||
/**
|
||||
* 本地缓存
|
||||
*/
|
||||
local: localCache
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import axios from 'axios'
|
||||
import { ElLoading, ElMessage } from 'element-plus'
|
||||
import { saveAs } from 'file-saver'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { blobValidate } from '@/utils/dkl'
|
||||
|
||||
const baseURL = import.meta.env.VITE_APP_BASE_API
|
||||
let downloadLoadingInstance;
|
||||
|
||||
export default {
|
||||
name(name, isDelete = true) {
|
||||
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
|
||||
axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
if (isBlob) {
|
||||
const blob = new Blob([res.data])
|
||||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
|
||||
} else {
|
||||
this.printErrMsg(res.data);
|
||||
}
|
||||
})
|
||||
},
|
||||
resource(resource) {
|
||||
var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource);
|
||||
axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
if (isBlob) {
|
||||
const blob = new Blob([res.data])
|
||||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
|
||||
} else {
|
||||
this.printErrMsg(res.data);
|
||||
}
|
||||
})
|
||||
},
|
||||
zip(url, name) {
|
||||
var url = baseURL + url
|
||||
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
|
||||
axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'blob',
|
||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
if (isBlob) {
|
||||
const blob = new Blob([res.data], { type: 'application/zip' })
|
||||
this.saveAs(blob, name)
|
||||
} else {
|
||||
this.printErrMsg(res.data);
|
||||
}
|
||||
downloadLoadingInstance.close();
|
||||
}).catch((r) => {
|
||||
console.error(r)
|
||||
ElMessage.error('下载文件出现错误,请联系管理员!')
|
||||
downloadLoadingInstance.close();
|
||||
})
|
||||
},
|
||||
saveAs(text, name, opts) {
|
||||
saveAs(text, name, opts);
|
||||
},
|
||||
async printErrMsg(data) {
|
||||
const resText = await data.text();
|
||||
const rspObj = JSON.parse(resText);
|
||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||
ElMessage.error(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import tab from './tab'
|
||||
import auth from './auth'
|
||||
import cache from './cache'
|
||||
import modal from './modal'
|
||||
import download from './download'
|
||||
|
||||
export default function installPlugins(app){
|
||||
// 页签操作
|
||||
app.config.globalProperties.$tab = tab
|
||||
// 认证对象
|
||||
app.config.globalProperties.$auth = auth
|
||||
// 缓存对象
|
||||
app.config.globalProperties.$cache = cache
|
||||
// 模态框对象
|
||||
app.config.globalProperties.$modal = modal
|
||||
// 下载文件
|
||||
app.config.globalProperties.$download = download
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
|
||||
|
||||
let loadingInstance;
|
||||
|
||||
export default {
|
||||
// 消息提示
|
||||
msg(content) {
|
||||
let options = {
|
||||
customClass: 'public-message',
|
||||
message: content,
|
||||
plain: true,
|
||||
}
|
||||
ElMessage.info(options)
|
||||
},
|
||||
// 错误消息
|
||||
msgError(content) {
|
||||
let options = {
|
||||
customClass: 'public-message',
|
||||
message: content,
|
||||
plain: true,
|
||||
}
|
||||
ElMessage.error(options)
|
||||
},
|
||||
// 成功消息
|
||||
msgSuccess(content) {
|
||||
let options = {
|
||||
customClass: 'public-message',
|
||||
message: content,
|
||||
plain: true,
|
||||
}
|
||||
ElMessage.success(options)
|
||||
},
|
||||
// 警告消息
|
||||
msgWarning(content) {
|
||||
let options = {
|
||||
customClass: 'public-message',
|
||||
message: content,
|
||||
plain: true,
|
||||
}
|
||||
ElMessage.warning(options)
|
||||
},
|
||||
// 弹出提示
|
||||
alert(content) {
|
||||
ElMessageBox.alert(content, "系统提示")
|
||||
},
|
||||
// 错误提示
|
||||
alertError(content) {
|
||||
ElMessageBox.alert(content, "系统提示", { type: 'error' })
|
||||
},
|
||||
// 成功提示
|
||||
alertSuccess(content) {
|
||||
ElMessageBox.alert(content, "系统提示", { type: 'success' })
|
||||
},
|
||||
// 警告提示
|
||||
alertWarning(content) {
|
||||
ElMessageBox.alert(content, "系统提示", { type: 'warning' })
|
||||
},
|
||||
// 通知提示
|
||||
notify(content) {
|
||||
ElNotification.info(content)
|
||||
},
|
||||
// 错误通知
|
||||
notifyError(content) {
|
||||
ElNotification.error(content);
|
||||
},
|
||||
// 成功通知
|
||||
notifySuccess(content) {
|
||||
ElNotification.success(content)
|
||||
},
|
||||
// 警告通知
|
||||
notifyWarning(content) {
|
||||
ElNotification.warning(content)
|
||||
},
|
||||
// 确认窗体
|
||||
confirm(content) {
|
||||
return ElMessageBox.confirm(content, "系统提示", {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: "warning",
|
||||
})
|
||||
},
|
||||
// 提交内容
|
||||
prompt(content) {
|
||||
return ElMessageBox.prompt(content, "系统提示", {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: "warning",
|
||||
})
|
||||
},
|
||||
// 打开遮罩层
|
||||
loading(content) {
|
||||
loadingInstance = ElLoading.service({
|
||||
lock: true,
|
||||
text: content,
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
})
|
||||
},
|
||||
// 关闭遮罩层
|
||||
closeLoading() {
|
||||
loadingInstance.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import useTagsViewStore from '@/store/modules/tagsView'
|
||||
import router from '@/router'
|
||||
|
||||
export default {
|
||||
// 刷新当前tab页签
|
||||
refreshPage(obj) {
|
||||
const { path, query, matched } = router.currentRoute.value;
|
||||
if (obj === undefined) {
|
||||
matched.forEach((m) => {
|
||||
if (m.components && m.components.default && m.components.default.name) {
|
||||
if (!['Layout', 'ParentView'].includes(m.components.default.name)) {
|
||||
obj = { name: m.components.default.name, path: path, query: query };
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return useTagsViewStore().delCachedView(obj).then(() => {
|
||||
const { path, query } = obj
|
||||
router.replace({
|
||||
path: '/redirect' + path,
|
||||
query: query
|
||||
})
|
||||
})
|
||||
},
|
||||
// 关闭当前tab页签,打开新页签
|
||||
closeOpenPage(obj) {
|
||||
useTagsViewStore().delView(router.currentRoute.value);
|
||||
if (obj !== undefined) {
|
||||
return router.push(obj);
|
||||
}
|
||||
},
|
||||
// 关闭指定tab页签
|
||||
closePage(obj) {
|
||||
if (obj === undefined) {
|
||||
return useTagsViewStore().delView(router.currentRoute.value).then(({ visitedViews }) => {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
if (latestView) {
|
||||
return router.push(latestView.fullPath)
|
||||
}
|
||||
return router.push('/');
|
||||
});
|
||||
}
|
||||
return useTagsViewStore().delView(obj);
|
||||
},
|
||||
// 关闭所有tab页签
|
||||
closeAllPage() {
|
||||
return useTagsViewStore().delAllViews();
|
||||
},
|
||||
// 关闭左侧tab页签
|
||||
closeLeftPage(obj) {
|
||||
return useTagsViewStore().delLeftTags(obj || router.currentRoute.value);
|
||||
},
|
||||
// 关闭右侧tab页签
|
||||
closeRightPage(obj) {
|
||||
return useTagsViewStore().delRightTags(obj || router.currentRoute.value);
|
||||
},
|
||||
// 关闭其他tab页签
|
||||
closeOtherPage(obj) {
|
||||
return useTagsViewStore().delOthersViews(obj || router.currentRoute.value);
|
||||
},
|
||||
// 打开tab页签
|
||||
openPage(url) {
|
||||
return router.push(url);
|
||||
},
|
||||
// 修改tab页签
|
||||
updatePage(obj) {
|
||||
return useTagsViewStore().updateVisitedView(obj);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
<template>
|
||||
<!-- 信息数据分析 -->
|
||||
<div class="chartBox">
|
||||
<div class="search largeScreen">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
|
||||
<el-row gutter="10">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="类型" prop="type" class="w100">
|
||||
<el-select
|
||||
class="largeScreen__select w100"
|
||||
popper-class="largeScreen__select"
|
||||
v-model="queryParams.type"
|
||||
placeholder="请选择分析类型"
|
||||
@change="changeTypeHandle"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="queryParams.type == '1'">
|
||||
<el-form-item label="年度" prop="year" class="w100">
|
||||
<el-date-picker
|
||||
v-model="queryParams.year"
|
||||
type="year"
|
||||
placeholder="请选择年份"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="queryParams.type == '2'">
|
||||
<el-form-item label="季度" prop="quarter" class="w100">
|
||||
<el-select
|
||||
class="largeScreen__select w100"
|
||||
popper-class="largeScreen__select"
|
||||
v-model="queryParams.quarter"
|
||||
placeholder="请选择分析类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in quarterOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="queryParams.type == '3'">
|
||||
<el-form-item label="月度" prop="month" class="w100">
|
||||
<el-date-picker
|
||||
v-model="queryParams.month"
|
||||
type="month"
|
||||
placeholder="请选择年份"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<!-- <el-button icon="Refresh" @click="resetQuery">重置</el-button> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<Echarts class="echarts" :option="initOption"></Echarts>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive, onMounted, nextTick, computed, getCurrentInstance} from "vue";
|
||||
import moment from "moment";
|
||||
import {fitChartSize, fitChartSizeVh} from "@/utils/styleUtils";
|
||||
const {proxy} = getCurrentInstance();
|
||||
|
||||
|
||||
|
||||
// 类型
|
||||
const queryParams = reactive({
|
||||
// pageNum: 1,
|
||||
// pageSize: 10,
|
||||
type: '1',
|
||||
year: '',
|
||||
quarter: '',
|
||||
month: '',
|
||||
})
|
||||
const typeOptions = reactive([
|
||||
{
|
||||
value: "1",
|
||||
label: "年度",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "季度",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "月度",
|
||||
},
|
||||
]);
|
||||
const quarterOptions = reactive([
|
||||
{
|
||||
value: "1",
|
||||
label: "第一季度",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "第二季度",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "第三季度",
|
||||
},
|
||||
{
|
||||
value: "4",
|
||||
label: "第四季度",
|
||||
},
|
||||
]);
|
||||
|
||||
function changeTypeHandle(e) {
|
||||
console.log("e",e)
|
||||
if (e === "1") {
|
||||
queryParams.year = formatDate(Date.now(), "YYYY");
|
||||
queryParams.quarter = "";
|
||||
queryParams.month = "";
|
||||
}else if(e === "2") {
|
||||
queryParams.year = "";
|
||||
queryParams.quarter = "1";
|
||||
queryParams.month = "";
|
||||
}else if(e === "3") {
|
||||
queryParams.year = "";
|
||||
queryParams.quarter = "";
|
||||
queryParams.month = formatDate(Date.now(), "YYYY-MM");
|
||||
}
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
// function resetQuery() {
|
||||
// dateRange.value = [];
|
||||
// proxy.resetForm("queryRef");
|
||||
// handleQuery();
|
||||
// }
|
||||
|
||||
function getList() {
|
||||
console.log("queryParams", queryParams)
|
||||
nextTick(() => {
|
||||
init();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function formatDate(str,type="yyyy-MM-dd") {
|
||||
return moment(str).format(type);
|
||||
}
|
||||
|
||||
const dataMap = reactive({
|
||||
streetData: [],
|
||||
streetPieData: [],
|
||||
});
|
||||
const minYear = ref("2021");
|
||||
const maxYear = ref(new Date().getFullYear());
|
||||
const yearList = ref([]);
|
||||
const fanZui_options = ref([
|
||||
{
|
||||
dictLabel: "危险作业罪",
|
||||
dictVal: "1",
|
||||
},
|
||||
{
|
||||
dictLabel: "重大责任事故罪",
|
||||
dictVal: "2",
|
||||
},
|
||||
{
|
||||
dictLabel: "危险驾驶罪",
|
||||
dictVal: "3",
|
||||
},
|
||||
{
|
||||
dictLabel: "信用卡诈骗罪等",
|
||||
dictVal: "4",
|
||||
},
|
||||
]);
|
||||
let types = ref("西安市");
|
||||
const dataList = ref([]);
|
||||
const xData = ref([]);
|
||||
const initOption = ref({});
|
||||
const echartsOptions = ref({});
|
||||
function randomHandle(min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
||||
function getValue(arr, key) {
|
||||
return arr.map((v) => v[key]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
var option;
|
||||
let xData = [...new Set(dataList.value.map((v) => v.street))];
|
||||
let seriesData = dataList.value.map((v) => ({
|
||||
value: v.amount,
|
||||
groupId: v.id,
|
||||
}));
|
||||
let seriesObj = {
|
||||
name: "各辖区统计",
|
||||
type: "bar",
|
||||
id: "sales",
|
||||
data: seriesData,
|
||||
itemStyle: {
|
||||
// 图形样式
|
||||
color: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
type: "linear",
|
||||
global: false,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "#826fe5",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#0c1f5699",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
let varColor = "#D1EDFF";
|
||||
initOption.value = {
|
||||
// legend: {
|
||||
// data: ["各辖区统计"],
|
||||
// textStyle: {
|
||||
// color: varColor,
|
||||
// },
|
||||
// },
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
axisPointer: {
|
||||
type: "shadow",
|
||||
},
|
||||
borderColor: "rgba(255,255,255,.3)",
|
||||
backgroundColor: "rgba(13,5,30,.6)",
|
||||
textStyle: {
|
||||
color: "white", //设置文字颜色
|
||||
},
|
||||
},
|
||||
// 图表定位
|
||||
grid: {
|
||||
top: fitChartSizeVh(70),
|
||||
left: fitChartSizeVh(100),
|
||||
right: fitChartSizeVh(100),
|
||||
bottom: fitChartSizeVh(30),
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
name: "区域",
|
||||
nameTextStyle: {
|
||||
color: varColor, // 设置Y轴名称的颜色
|
||||
},
|
||||
data: xData,
|
||||
axisTick: {
|
||||
//x轴刻度线
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
//x轴坐标轴
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: varColor,
|
||||
fontSize: fitChartSize(30),
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
name: "数量",
|
||||
nameTextStyle: {
|
||||
color: varColor, // 设置Y轴名称的颜色为红色
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: ["#00A8FF"],
|
||||
type: "dashed",
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: varColor,
|
||||
},
|
||||
},
|
||||
// dataGroupId: "",
|
||||
animationDurationUpdate: 500,
|
||||
series: [
|
||||
{
|
||||
...seriesObj,
|
||||
},
|
||||
{
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
graphic: [
|
||||
{
|
||||
type: "text",
|
||||
style: {
|
||||
text: "",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function getYearList() {
|
||||
let arr = [];
|
||||
for (var year = minYear.value; year <= maxYear.value; year++) {
|
||||
arr.push(year);
|
||||
}
|
||||
yearList.value = arr;
|
||||
}
|
||||
function testHandle() {
|
||||
let arr = [];
|
||||
let list = [
|
||||
// 各辖区,
|
||||
"未央区",
|
||||
"新城区",
|
||||
"碑林区",
|
||||
"莲湖区",
|
||||
"灞桥区",
|
||||
"雁塔区",
|
||||
"阎良区",
|
||||
"临潼区",
|
||||
"长安区",
|
||||
"高陵区",
|
||||
"鄠邑区",
|
||||
"蓝田县",
|
||||
"周至县",
|
||||
];
|
||||
let list2 = [
|
||||
// 各街道办,
|
||||
"三桥街道",
|
||||
"建章路街道",
|
||||
"六村堡街道",
|
||||
"未央宫街道",
|
||||
"汉城街道",
|
||||
"草滩街道",
|
||||
"未央湖街道",
|
||||
"徐家湾街道",
|
||||
"张家堡街道",
|
||||
"谭家街道",
|
||||
"大明宫街道",
|
||||
"辛家庙街道",
|
||||
];
|
||||
let lists = [];
|
||||
if (types.value == "西安市") {
|
||||
lists = list;
|
||||
} else {
|
||||
lists = list2;
|
||||
}
|
||||
lists.forEach((v, i) => {
|
||||
arr.push({
|
||||
id: "00" + i,
|
||||
street: v,
|
||||
age: randomHandle(20, 25),
|
||||
amount: randomHandle(30, 55),
|
||||
index: i,
|
||||
outsideData: randomHandle(10, 20),
|
||||
picketAmount: randomHandle(10, 25),
|
||||
});
|
||||
});
|
||||
dataList.value = arr;
|
||||
}
|
||||
onMounted(() => {
|
||||
xData.value = getValue(fanZui_options.value, "dictLabel");
|
||||
testHandle();
|
||||
getYearList();
|
||||
changeTypeHandle('1');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
.chartBox {
|
||||
height: 100%;
|
||||
.echarts {
|
||||
height: calc(100% - calculate.vh(50px)) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,462 @@
|
|||
<template>
|
||||
<!-- 只保留一个图表容器 -->
|
||||
<div ref="chartContainer" class="chartContainer"></div>
|
||||
|
||||
<!-- 模态框部分保持不变 -->
|
||||
<LargeScreenModal
|
||||
v-model:open="chargeOpen"
|
||||
:title="modalTitle"
|
||||
>
|
||||
<div class="tableWrap largeScreen">
|
||||
<el-table
|
||||
class="tableData"
|
||||
row-class-name="table-row"
|
||||
:data="tableData"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<el-table-column
|
||||
label="名称"
|
||||
show-overflow-tooltip=""
|
||||
align="center"
|
||||
prop="pointName"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="值班人员"
|
||||
show-overflow-tooltip=""
|
||||
align="center"
|
||||
prop="dutyPeople"
|
||||
/>
|
||||
<el-table-column label="值班联系方式" align="center" prop="dutyPhone" />
|
||||
<el-table-column
|
||||
label="地址"
|
||||
show-overflow-tooltip
|
||||
align="center"
|
||||
prop="pointAddress"
|
||||
/>
|
||||
<el-table-column
|
||||
label="经度"
|
||||
show-overflow-tooltip
|
||||
align="center"
|
||||
prop="lng"
|
||||
/>
|
||||
<el-table-column
|
||||
label="纬度"
|
||||
show-overflow-tooltip
|
||||
align="center"
|
||||
prop="lat"
|
||||
/>
|
||||
<el-table-column
|
||||
label="最大承载量"
|
||||
align="center"
|
||||
prop="loadBearingMax"
|
||||
/>
|
||||
</el-table>
|
||||
<pagination
|
||||
class="largeScreen__pagination"
|
||||
popperClass="largeScreen__pagination"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</LargeScreenModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
getCurrentInstance,
|
||||
} from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import LargeScreenModal from "../largeScreenModal.vue";
|
||||
import { fitChartSize, fitChartSizeVh } from "@/utils/styleUtils";
|
||||
import { getallList } from "@/api/InfoOverview.js";
|
||||
import { getzongshuDetail } from "@/api/map.js";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useResizeObserver } from "@vueuse/core";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_yes_no } = proxy.useDict("sys_yes_no");
|
||||
|
||||
const props = defineProps({
|
||||
time: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 图表相关
|
||||
const chartContainer = ref(null);
|
||||
let chartInstance = null;
|
||||
|
||||
// 模态框相关
|
||||
const chargeOpen = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
regionname: null,
|
||||
});
|
||||
const tableData = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
// 处理数据,生成堆叠柱状图需要的格式
|
||||
const processStackedBarData = (rawData) => {
|
||||
const targetTypes = ["重点场所", "安保力量", "交通枢纽", "景区"];
|
||||
const regionMap = {};
|
||||
|
||||
rawData.forEach((item) => {
|
||||
const region = item.regionname;
|
||||
const type = item.typename || "无类型";
|
||||
|
||||
if (!regionMap[region]) {
|
||||
regionMap[region] = {
|
||||
重点场所: 0,
|
||||
安保力量: 0,
|
||||
交通枢纽: 0,
|
||||
景区: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (targetTypes.includes(type)) {
|
||||
const count = item.loadBearingMax ? parseInt(item.loadBearingMax) : 1;
|
||||
regionMap[region][type] += count;
|
||||
}
|
||||
});
|
||||
|
||||
const regions = Object.keys(regionMap);
|
||||
const seriesData = targetTypes.map((type) => ({
|
||||
name: type,
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
emphasis: { focus: "series" },
|
||||
data: regions.map((region) => regionMap[region][type]),
|
||||
itemStyle: {
|
||||
color: getSeriesColor(targetTypes.indexOf(type)),
|
||||
},
|
||||
}));
|
||||
|
||||
return { regions, seriesData };
|
||||
};
|
||||
|
||||
// 获取系列颜色
|
||||
const getSeriesColor = (index) => {
|
||||
const colors = [
|
||||
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#409EFF" },
|
||||
{ offset: 1, color: "#409EFF99" },
|
||||
]),
|
||||
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#67C23A" },
|
||||
{ offset: 1, color: "#67C23AAA" },
|
||||
]),
|
||||
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#2CBF04" },
|
||||
{ offset: 1, color: "#2CBF04AA" },
|
||||
]),
|
||||
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#F56C6C" },
|
||||
{ offset: 1, color: "#F56C6CAA" },
|
||||
]),
|
||||
];
|
||||
return colors[index % colors.length];
|
||||
};
|
||||
const paramsseriesName = ref('');
|
||||
const paramsname = ref('');
|
||||
const name1 = ref('');
|
||||
const name2 = ref('');
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartContainer.value) return;
|
||||
|
||||
// 先销毁旧实例
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(chartContainer.value);
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: "",
|
||||
left: "center",
|
||||
textStyle: {
|
||||
color: "#D1EDFF",
|
||||
fontSize: fitChartSize(16),
|
||||
},
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'slider', // 滑动条型dataZoom
|
||||
show: true,
|
||||
xAxisIndex: [0],
|
||||
start: 0, // 默认起始位置0%
|
||||
end: 30, // 默认显示前30%的数据(约6-7个市区)
|
||||
height: fitChartSizeVh(40), // 滑动条高度
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: fitChartSizeVh(20), // 距离底部距离
|
||||
fillerColor: 'rgba(0, 168, 255, 0.5)', // 选中区域填充颜色
|
||||
borderColor: 'transparent',
|
||||
handleStyle: {
|
||||
color: '#00A8FF' // 手柄颜色
|
||||
},
|
||||
textStyle: {
|
||||
color: '#D1EDFF' // 文字颜色
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'inside', // 内置型dataZoom,可以用鼠标滚轮缩放
|
||||
xAxisIndex: [0],
|
||||
zoomOnMouseWheel: false, // 关闭滚轮缩放
|
||||
moveOnMouseWheel: true, // 开启滚轮平移
|
||||
moveOnMouseMove: true // 开启鼠标拖动平移
|
||||
}
|
||||
],
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
className: "toolTip",
|
||||
formatter: function (params) {
|
||||
console.log(params);
|
||||
let str = "";
|
||||
str += `<div class="chartsBox">
|
||||
<div class="chartsBox__title">${params[0].axisValue}</div>
|
||||
<div class="chartsBox__container">`;
|
||||
params.forEach(v => {
|
||||
str += `
|
||||
<div class="chartsBox__container-item flex">
|
||||
<div class="chartsBox__container-item-label flex">
|
||||
${v.marker}
|
||||
${v.seriesName}
|
||||
</div>
|
||||
<div class="chartsBox__container-item-value">${v.value}</div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
str += `</div>
|
||||
</div>
|
||||
`;
|
||||
return str
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: fitChartSizeVh(20),
|
||||
left: fitChartSize(20),
|
||||
right: fitChartSize(20),
|
||||
bottom: fitChartSizeVh(80),
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
axisLabel: {
|
||||
color: "#D1EDFF",
|
||||
rotate: 30,
|
||||
fontSize: fitChartSize(28),
|
||||
formatter: (value) =>
|
||||
value.length > 4 ? value.substring(0, 4) + "..." : value,
|
||||
},
|
||||
axisLine: { lineStyle: { color: "#FFF" } },
|
||||
axisTick: { show: false },
|
||||
},
|
||||
yAxis: {
|
||||
type: "value",
|
||||
name: "数量",
|
||||
nameTextStyle: { color: "#D1EDFF" },
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: ["#FFF"],
|
||||
type: "dashed",
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
axisLabel: { color: "#D1EDFF", fontSize: fitChartSize(26) },
|
||||
axisLine: { show: false, lineStyle: { color: "#00A8FF" } },
|
||||
},
|
||||
series: [],
|
||||
};
|
||||
// 添加点击事件监听
|
||||
chartInstance.on("click", (params) => {
|
||||
console.log(params);
|
||||
paramsseriesName.value = params.seriesName;
|
||||
paramsname.value = params.name;
|
||||
let par = {
|
||||
typename: paramsseriesName.value,
|
||||
regionname: paramsname.value ,
|
||||
};
|
||||
getList()
|
||||
});
|
||||
chartInstance.setOption(option);
|
||||
window.addEventListener("resize", () => {
|
||||
chartInstance.resize();
|
||||
});
|
||||
};
|
||||
|
||||
// 更新图表数据
|
||||
const updateChart = (data) => {
|
||||
if (!chartInstance) return;
|
||||
|
||||
const { regions, seriesData } = processStackedBarData(data);
|
||||
|
||||
chartInstance.setOption({
|
||||
xAxis: { data: regions },
|
||||
series: seriesData,
|
||||
});
|
||||
};
|
||||
|
||||
// 响应式调整图表大小
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 获取数据
|
||||
const fetchData = () => {
|
||||
const params = {
|
||||
delFlag: 0,
|
||||
monitoringType: 2,
|
||||
};
|
||||
|
||||
getallList(params).then((res) => {
|
||||
updateChart(res.rows);
|
||||
});
|
||||
};
|
||||
|
||||
// 模态框相关方法
|
||||
const getList = () => {
|
||||
let par = {
|
||||
typename: paramsseriesName.value,
|
||||
regionname: paramsname.value,
|
||||
pageSize:queryParams.pageSize,
|
||||
pageNum:queryParams.pageNum,
|
||||
};
|
||||
getzongshuDetail(par).then((res) => {
|
||||
tableData.value = res.rows;
|
||||
total.value = res.total;
|
||||
modalTitle.value = "大客流信息概览" +'('+ paramsname.value + '-'+ paramsseriesName.value + ')';
|
||||
chargeOpen.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
const handleRowClick = (row) => {
|
||||
console.log(row);
|
||||
|
||||
// router.push({
|
||||
// path: "/BasicInformation/DklMonitoringPoints",
|
||||
// query: { id: row.id },
|
||||
// });
|
||||
};
|
||||
|
||||
const handleDetail = (row) => {
|
||||
console.log(row);
|
||||
|
||||
// queryParams.regionname = row.data.groupId;
|
||||
// getList();
|
||||
};
|
||||
|
||||
// 监听时间变化
|
||||
watch(
|
||||
() => props.time,
|
||||
(newTime) => {
|
||||
console.log("时间变化:", newTime);
|
||||
fetchData();
|
||||
}
|
||||
);
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
fetchData();
|
||||
|
||||
// 使用vueuse的useResizeObserver实现更好的响应式
|
||||
useResizeObserver(chartContainer, handleResize);
|
||||
});
|
||||
|
||||
// 清理
|
||||
onBeforeUnmount(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
// :deep(.el-table__body-wrapper) {
|
||||
// background: #29689E !important;
|
||||
// }
|
||||
:deep(.cell) {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* 图表容器样式 */
|
||||
// div[ref="chartContainer"] {
|
||||
// transition: all 0.3s ease;
|
||||
// }
|
||||
.chartContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.tableWrap {
|
||||
:deep(td.el-table__cell) {
|
||||
border-bottom: calculate.px2font(1px) solid #888;
|
||||
}
|
||||
:deep(.table-row:hover) {
|
||||
box-shadow: inset 0 0 0 calculate.px2font(3px) #266FFF;
|
||||
background-color: transparent;
|
||||
}
|
||||
// :deep(tr) {
|
||||
// background-color: transparent !important;
|
||||
// }
|
||||
}
|
||||
.chartContainer {
|
||||
&:deep(.toolTip) {
|
||||
padding: 0 !important;
|
||||
box-shadow: none !important;
|
||||
border: 0 !important;
|
||||
border-radius: calculate.px2font(5px) !important;
|
||||
|
||||
.chartsBox {
|
||||
line-height: 1;
|
||||
padding: calculate.vh(15px) calculate.vw(15px);
|
||||
.chartsBox__title {
|
||||
font-size: calculate.px2font(16px);
|
||||
margin-bottom: calculate.vh(10px);
|
||||
}
|
||||
.chartsBox__container {
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
&-item {
|
||||
display: flex;
|
||||
gap: calculate.vw(20px);
|
||||
justify-content: space-between;
|
||||
font-size: calculate.px2font(14px);
|
||||
line-height: inherit;
|
||||
padding: calculate.vh(5px) 0;
|
||||
|
||||
&-label {
|
||||
gap: calculate.vw(5px);
|
||||
span {
|
||||
display: block;
|
||||
width: calculate.vh(10px) !important;
|
||||
height: calculate.vh(10px) !important;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<el-dropdown class="dropdown" popper-class="largeScreen__dropdown" trigger="click" @command="dropDownHandle">
|
||||
<span class="el-dropdown-link">
|
||||
<img src="@/assets/images/largeScreenImage/more@2x.png" alt="">
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="v in options" :command="v.value">
|
||||
{{ v.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, getCurrentInstance} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
options: {
|
||||
required: true,
|
||||
default: () => []
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(["command"]);
|
||||
|
||||
function dropDownHandle(e) {
|
||||
emit("command",e)
|
||||
}
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
.dropdown {
|
||||
.el-dropdown-link {
|
||||
img {
|
||||
width: calculate.vw(15px);
|
||||
height: calculate.vw(15px);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
<template>
|
||||
<!-- 信息数据分析 -->
|
||||
<div class="chartBox">
|
||||
<div class="search largeScreen">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
|
||||
<el-row gutter="10">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="类型" prop="type" class="w100">
|
||||
<el-select
|
||||
class="largeScreen__select w100"
|
||||
popper-class="largeScreen__select"
|
||||
v-model="queryParams.type"
|
||||
placeholder="请选择分析类型"
|
||||
@change="changeTypeHandle"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="queryParams.type == '1'">
|
||||
<el-form-item label="年度" prop="year" class="w100">
|
||||
<el-date-picker
|
||||
v-model="queryParams.year"
|
||||
type="year"
|
||||
placeholder="请选择年份"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="queryParams.type == '2'">
|
||||
<el-form-item label="季度" prop="quarter" class="w100">
|
||||
<el-select
|
||||
class="largeScreen__select w100"
|
||||
popper-class="largeScreen__select"
|
||||
v-model="queryParams.quarter"
|
||||
placeholder="请选择分析类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in quarterOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="queryParams.type == '3'">
|
||||
<el-form-item label="月度" prop="month" class="w100">
|
||||
<el-date-picker
|
||||
v-model="queryParams.month"
|
||||
type="month"
|
||||
placeholder="请选择年份"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<!-- <el-button icon="Refresh" @click="resetQuery">重置</el-button> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<Echarts class="echarts" :option="initOption"></Echarts>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive, onMounted, nextTick, computed, getCurrentInstance} from "vue";
|
||||
import moment from "moment";
|
||||
import {fitChartSize, fitChartSizeVh} from "@/utils/styleUtils";
|
||||
const {proxy} = getCurrentInstance();
|
||||
|
||||
|
||||
|
||||
// 类型
|
||||
const queryParams = reactive({
|
||||
// pageNum: 1,
|
||||
// pageSize: 10,
|
||||
type: '1',
|
||||
year: '',
|
||||
quarter: '',
|
||||
month: '',
|
||||
})
|
||||
const typeOptions = reactive([
|
||||
{
|
||||
value: "1",
|
||||
label: "年度",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "季度",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "月度",
|
||||
},
|
||||
]);
|
||||
const quarterOptions = reactive([
|
||||
{
|
||||
value: "1",
|
||||
label: "第一季度",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "第二季度",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "第三季度",
|
||||
},
|
||||
{
|
||||
value: "4",
|
||||
label: "第四季度",
|
||||
},
|
||||
]);
|
||||
|
||||
function changeTypeHandle(e) {
|
||||
console.log("e",e)
|
||||
if (e === "1") {
|
||||
queryParams.year = formatDate(Date.now(), "YYYY");
|
||||
queryParams.quarter = "";
|
||||
queryParams.month = "";
|
||||
}else if(e === "2") {
|
||||
queryParams.year = "";
|
||||
queryParams.quarter = "1";
|
||||
queryParams.month = "";
|
||||
}else if(e === "3") {
|
||||
queryParams.year = "";
|
||||
queryParams.quarter = "";
|
||||
queryParams.month = formatDate(Date.now(), "YYYY-MM");
|
||||
}
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
// function resetQuery() {
|
||||
// dateRange.value = [];
|
||||
// proxy.resetForm("queryRef");
|
||||
// handleQuery();
|
||||
// }
|
||||
|
||||
function getList() {
|
||||
console.log("queryParams", queryParams)
|
||||
nextTick(() => {
|
||||
init();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function formatDate(str,type="yyyy-MM-dd") {
|
||||
return moment(str).format(type);
|
||||
}
|
||||
|
||||
const dataMap = reactive({
|
||||
streetData: [],
|
||||
streetPieData: [],
|
||||
});
|
||||
const minYear = ref("2021");
|
||||
const maxYear = ref(new Date().getFullYear());
|
||||
const yearList = ref([]);
|
||||
const fanZui_options = ref([
|
||||
{
|
||||
dictLabel: "危险作业罪",
|
||||
dictVal: "1",
|
||||
},
|
||||
{
|
||||
dictLabel: "重大责任事故罪",
|
||||
dictVal: "2",
|
||||
},
|
||||
{
|
||||
dictLabel: "危险驾驶罪",
|
||||
dictVal: "3",
|
||||
},
|
||||
{
|
||||
dictLabel: "信用卡诈骗罪等",
|
||||
dictVal: "4",
|
||||
},
|
||||
]);
|
||||
let types = ref("西安市");
|
||||
const dataList = ref([]);
|
||||
const xData = ref([]);
|
||||
const initOption = ref({});
|
||||
const echartsOptions = ref({});
|
||||
function randomHandle(min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min;
|
||||
}
|
||||
function getValue(arr, key) {
|
||||
return arr.map((v) => v[key]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
var option;
|
||||
let xData = [...new Set(dataList.value.map((v) => v.street))];
|
||||
let seriesData = dataList.value.map((v) => ({
|
||||
value: v.amount,
|
||||
groupId: v.id,
|
||||
}));
|
||||
let seriesObj = {
|
||||
name: "各辖区统计",
|
||||
type: "bar",
|
||||
id: "sales",
|
||||
data: seriesData,
|
||||
itemStyle: {
|
||||
// 图形样式
|
||||
color: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
type: "linear",
|
||||
global: false,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: "#826fe5",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: "#0c1f5699",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
let varColor = "#D1EDFF";
|
||||
initOption.value = {
|
||||
// legend: {
|
||||
// data: ["各辖区统计"],
|
||||
// textStyle: {
|
||||
// color: varColor,
|
||||
// },
|
||||
// },
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
axisPointer: {
|
||||
type: "shadow",
|
||||
},
|
||||
borderColor: "rgba(255,255,255,.3)",
|
||||
backgroundColor: "rgba(13,5,30,.6)",
|
||||
textStyle: {
|
||||
color: "white", //设置文字颜色
|
||||
},
|
||||
},
|
||||
// 图表定位
|
||||
grid: {
|
||||
top: fitChartSizeVh(70),
|
||||
left: fitChartSizeVh(100),
|
||||
right: fitChartSizeVh(100),
|
||||
bottom: fitChartSizeVh(30),
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
name: "区域",
|
||||
nameTextStyle: {
|
||||
color: varColor, // 设置Y轴名称的颜色
|
||||
},
|
||||
data: xData,
|
||||
axisTick: {
|
||||
//x轴刻度线
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
//x轴坐标轴
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
color: varColor,
|
||||
fontSize: fitChartSize(30),
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
name: "数量",
|
||||
nameTextStyle: {
|
||||
color: varColor, // 设置Y轴名称的颜色为红色
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: ["#00A8FF"],
|
||||
type: "dashed",
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
axisLabel: {
|
||||
color: varColor,
|
||||
},
|
||||
},
|
||||
// dataGroupId: "",
|
||||
animationDurationUpdate: 500,
|
||||
series: [
|
||||
{
|
||||
...seriesObj,
|
||||
},
|
||||
{
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
graphic: [
|
||||
{
|
||||
type: "text",
|
||||
style: {
|
||||
text: "",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function getYearList() {
|
||||
let arr = [];
|
||||
for (var year = minYear.value; year <= maxYear.value; year++) {
|
||||
arr.push(year);
|
||||
}
|
||||
yearList.value = arr;
|
||||
}
|
||||
function testHandle() {
|
||||
let arr = [];
|
||||
let list = [
|
||||
"未央区",
|
||||
"新城区",
|
||||
"碑林区",
|
||||
"莲湖区",
|
||||
"灞桥区",
|
||||
"雁塔区",
|
||||
"阎良区",
|
||||
"临潼区",
|
||||
"长安区",
|
||||
"高陵区",
|
||||
"鄠邑区",
|
||||
"蓝田县",
|
||||
"周至县",
|
||||
];
|
||||
|
||||
list.forEach((v, i) => {
|
||||
arr.push({
|
||||
id: "00" + i,
|
||||
street: v,
|
||||
age: randomHandle(20, 25),
|
||||
amount: randomHandle(30, 55),
|
||||
index: i,
|
||||
outsideData: randomHandle(10, 20),
|
||||
picketAmount: randomHandle(10, 25),
|
||||
});
|
||||
});
|
||||
dataList.value = arr;
|
||||
}
|
||||
onMounted(() => {
|
||||
xData.value = getValue(fanZui_options.value, "dictLabel");
|
||||
testHandle();
|
||||
getYearList();
|
||||
changeTypeHandle('1');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
.chartBox {
|
||||
height: 100%;
|
||||
.echarts {
|
||||
height: calc(100% - calculate.vh(50px)) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,12 @@
|
|||
// utils/date.js
|
||||
export const formatDate = (date) => {
|
||||
const d = new Date(date)
|
||||
return d.toISOString().split('T')[0] // 返回 YYYY-MM-DD 格式
|
||||
}
|
||||
|
||||
export const getLastMonthToday = () => {
|
||||
const today = new Date()
|
||||
const lastMonth = new Date(today)
|
||||
lastMonth.setMonth(lastMonth.getMonth() - 1)
|
||||
return lastMonth
|
||||
}
|
||||
|
|
@ -0,0 +1,554 @@
|
|||
<template>
|
||||
<div class="largeScreen">
|
||||
<el-dialog
|
||||
class="largeScreen__dialog"
|
||||
v-model="visible"
|
||||
:width="width"
|
||||
:show-close="false"
|
||||
append-to-body
|
||||
@opened="openedHandle"
|
||||
@close="closeHandle"
|
||||
>
|
||||
<div class="largeScreen__dialog-content" :style="{ '--height--' : height }">
|
||||
<div class="header">
|
||||
<div class="header-title">
|
||||
<!-- <el-icon size="16"><CopyDocument /></el-icon> -->
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
<div class="header-close" @click="closeHandle">
|
||||
<el-icon size="24"><Close /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- v-if="contentShow" -->
|
||||
<div class="container">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div></div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive, onMounted, computed, getCurrentInstance} from "vue";
|
||||
import { fitChartSize, fitChartSizeVh } from "@/utils/styleUtils";
|
||||
import styleUtil from "@/utils/styleUtils";
|
||||
const props = defineProps({
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
width: {
|
||||
type: String || Number,
|
||||
default: styleUtil.px2vw(900),
|
||||
},
|
||||
height: {
|
||||
type: String || Number,
|
||||
default: styleUtil.px2vh(700),
|
||||
},
|
||||
title: {
|
||||
required: true,
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
const contentShow = ref(false);
|
||||
const visible = computed(() => props.open);
|
||||
|
||||
function openedHandle() {
|
||||
contentShow.value = true;
|
||||
}
|
||||
|
||||
// 添加emit事件
|
||||
const emit = defineEmits(["update:open"]);
|
||||
|
||||
// 确定
|
||||
const closeHandle = () => {
|
||||
contentShow.value = false;
|
||||
emit("update:open", false);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
contentShow,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
// 大屏样式
|
||||
$shadow-color: #127BDC;
|
||||
$dialog__title-color: #00f4f5;
|
||||
$dp-primary-color: #11396A;
|
||||
$text__color: #F4F4F4;
|
||||
$pager__bg-color: linear-gradient(rgba(0, 240, 255, 0.1), rgba(13, 172, 255, 0.1));
|
||||
$pager__bg-color-isActive: #127BDC;
|
||||
$header__height: calculate.vh(40px);
|
||||
$clip__size: calculate.px2font(15px);
|
||||
.largeScreen {
|
||||
// 大屏弹框
|
||||
&__dialog {
|
||||
background-color: $dp-primary-color;
|
||||
// padding: calculate.vh(20px) calculate.vh(30px);
|
||||
padding: calculate.vh(15px);
|
||||
border-radius: calculate.px2font(0px);
|
||||
border: calculate.px2font(1px) solid rgba(14, 255, 255, 0.6);
|
||||
box-shadow: 0px 0px calculate.px2font(100px) 0 #4790f4;
|
||||
// box-shadow: inset 0 0 calculate.px2font(20px) $shadow-color; /* 向内阴影,颜色为深红色 */
|
||||
max-height: fit-content;
|
||||
clip-path: polygon($clip__size 0, calc(100% - $clip__size) 0, 100% $clip__size, 100% 100%, 100% 100%, 0 100%, 0 100%, 0 $clip__size);
|
||||
&::before,&::after {
|
||||
content: "";
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: calculate.px2font(25px);
|
||||
height: calculate.px2font(25px);
|
||||
border-color: #84E0FF;
|
||||
border-style: solid;
|
||||
}
|
||||
&::before {
|
||||
left: 0;
|
||||
border-width: 0 0 calculate.px2font(3px) calculate.px2font(3px);
|
||||
}
|
||||
&::after {
|
||||
right: 0;
|
||||
border-width: 0 calculate.px2font(3px) calculate.px2font(3px) 0;
|
||||
}
|
||||
|
||||
.el-dialog__header,.el-dialog__footer {
|
||||
display: none;
|
||||
}
|
||||
.show-close {
|
||||
display: none;
|
||||
}
|
||||
&-content {
|
||||
width: 100%;
|
||||
// height: var(--height--);
|
||||
.header {
|
||||
position: relative;
|
||||
// border-bottom: calculate.px2font(2px) solid #072a52;
|
||||
margin-bottom: calculate.px2font(15px);
|
||||
&-title {
|
||||
height: $header__height;
|
||||
color: $dialog__title-color;
|
||||
font-size: calculate.px2font(16px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calculate.px2font(10px);
|
||||
|
||||
.el-icon {
|
||||
font-size: calculate.px2font(20px) !important;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
&-close {
|
||||
position: absolute;
|
||||
top: calculate.vh(10px);
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
border-radius: 50%;
|
||||
// padding: calculate.vh(3px) calculate.vw(5px);
|
||||
// background-color: #0c3573;
|
||||
border: calculate.px2font(1px) solid #D8D8D8;
|
||||
color: #FFF;
|
||||
width: calculate.vh(30px);
|
||||
height: calculate.vh(30px);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
font-size: calculate.px2font(16px) !important;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.container {
|
||||
height: calc(#{var(--height--)} - calculate.vh(60px));
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
.tableWrap {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.el-dialog__body {
|
||||
padding: 0;
|
||||
overflow-y: visible;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
.el-scrollbar__bar {
|
||||
border-radius: calculate.px2font(9999px);
|
||||
height: calculate.vh(10px);
|
||||
}
|
||||
|
||||
.el-card {
|
||||
.el-card__body {
|
||||
--el-font-size-base: #{calculate.px2font(14px)};
|
||||
padding: calculate.vh(14px) calculate.vw(20px) calculate.vh(20px) calculate.vw(20px) !important;
|
||||
|
||||
h4,p {
|
||||
margin: calculate.vh(14px) 0;
|
||||
font-size: calculate.px2font(16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-timeline {
|
||||
.el-timeline-item {
|
||||
.el-timeline-item__node--normal {
|
||||
--el-timeline-node-size-normal: #{calculate.px2font(12px)};
|
||||
}
|
||||
.el-timeline-item__tail {
|
||||
left: calculate.px2font(4px);
|
||||
border-left-width: calculate.px2font(2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-table {
|
||||
--el-table-header-bg-color: #1A87D2;
|
||||
--el-table-bg-color: #005E99;
|
||||
--el-table-border-color: transparent;
|
||||
--el-table-border: 0px solid #043576;
|
||||
--el-table-text-color: #FFF;
|
||||
// --el-table-header-text-color: #98C8FF;
|
||||
--el-table-tr-bg-color: #325b81;
|
||||
--el-table-row-hover-bg-color: #0dacff4d;
|
||||
|
||||
$tableBg: #005E99;
|
||||
|
||||
height: calc(100% - calculate.vh(70px)) !important;
|
||||
font-size: calculate.px2font(14px);
|
||||
|
||||
border-radius: 0;
|
||||
|
||||
.el-table__inner-wrapper {
|
||||
height: 100% !important;
|
||||
background: $tableBg !important;
|
||||
}
|
||||
.el-table__header-wrapper th,
|
||||
.el-table__fixed-header-wrapper th {
|
||||
font-size: calculate.px2font(16px) !important;
|
||||
height: calculate.vh(30px) !important;
|
||||
padding: calculate.vh(10px) calculate.vh(12px);
|
||||
// background-color: var(--el-table-header-bg-color) !important;
|
||||
background-color: transparent !important;
|
||||
color: var(--el-table-header-text-color);
|
||||
}
|
||||
thead {
|
||||
tr {
|
||||
background: linear-gradient(to bottom, #56CCF2, #2F80ED);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
.cell{
|
||||
padding: 0 calculate.vw(10px);
|
||||
line-height: calculate.vh(23px);
|
||||
}
|
||||
.el-table__cell{
|
||||
padding: calculate.vh(15px) 0;
|
||||
}
|
||||
.el-table__empty-block {
|
||||
height: 100%;
|
||||
}
|
||||
tr {
|
||||
background-color: $tableBg;
|
||||
&:hover {
|
||||
background-color: $tableBg;
|
||||
}
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #3C93D2;
|
||||
&:hover {
|
||||
background-color: #3C93D2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: calculate.vh(20px) 0 0 0;
|
||||
.el-pagination {
|
||||
$buttonWH: calculate.vh(40px);
|
||||
--el-pagination-font-size: #{calculate.px2font(14px)};
|
||||
--el-pagination-button-width: #{$buttonWH};
|
||||
--el-pagination-button-height: #{$buttonWH};
|
||||
--el-pagination-border-radius: #{calculate.px2font(3px)};
|
||||
--el-input-border-radius: #{calculate.px2font(5px)};
|
||||
--el-input-inner-height: calc(#{calculate.vh(32px)} - #{calculate.vh(2px)});
|
||||
--el-pagination-item-gap: #{calculate.px2font(12px)};
|
||||
|
||||
margin-right: calculate.vw(10px);
|
||||
|
||||
button {
|
||||
// border-radius: var(--el-pagination-border-radius) !important;
|
||||
padding: 0 calculate.vh(4px);
|
||||
.el-icon {
|
||||
font-size: calculate.px2font(18px);
|
||||
}
|
||||
}
|
||||
&.is-background .el-pager li.is-active {
|
||||
border-radius: var(--el-pagination-border-radius);
|
||||
}
|
||||
&.is-background {
|
||||
.btn-next, .btn-prev, .el-pager li {
|
||||
margin: 0 calculate.vw(4px);
|
||||
}
|
||||
}
|
||||
&__jump {
|
||||
.el-pagination__editor.el-input {
|
||||
width: calculate.vw(56px);
|
||||
}
|
||||
.el-input__wrapper {
|
||||
padding: calculate.vh(1px) calculate.vh(11px);
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) var(--el-input-border-color,var(--el-border-color)) inset;
|
||||
border-radius: var(--el-pagination-border-radius);
|
||||
.el-input__inner {
|
||||
height: calculate.vh(35px);
|
||||
font-size: calculate.px2font(14px);
|
||||
border-radius: var(--el-pagination-border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-select {
|
||||
width: calculate.vw(128px);
|
||||
.el-select__wrapper {
|
||||
min-height: calculate.vh(40px);
|
||||
line-height: calculate.vh(24px);
|
||||
font-size: calculate.px2font(14px);
|
||||
padding: calculate.vh(4px) calculate.vh(12px);
|
||||
border-radius: calculate.px2font(4px);
|
||||
}
|
||||
.el-select__suffix {
|
||||
.el-icon {
|
||||
width: calculate.vw(20px);
|
||||
height: calculate.vh(20px);
|
||||
line-height: calculate.vh(20px);
|
||||
font-size: calculate.px2font(14px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
&__select, &__pagination {
|
||||
// 下拉气泡三角颜色
|
||||
&[data-popper-placement^=bottom] .el-popper__arrow::before {
|
||||
background: #254277;
|
||||
border: calculate.px2font(1px) solid #254277;
|
||||
}
|
||||
|
||||
.el-select__wrapper {
|
||||
background-color: $dp-primary-color; // 外层下拉框背景
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) #254277 inset;
|
||||
|
||||
&:hover{
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) $shadow-color inset;
|
||||
}
|
||||
}
|
||||
.el-select__placeholder {
|
||||
color: $text__color;
|
||||
&.is-transparent {
|
||||
// color: rgb(244, 244, 244, .5);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-light {
|
||||
border-color: #00a2ff;
|
||||
background-color: rgba(0, 120, 233, 0.63) ; // 外层下拉框背景
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
&__wrap {
|
||||
border: none;
|
||||
// background-color: $dp-primary-color; // 下拉框背景色
|
||||
background-color: linear-gradient(0deg, #00B4FF 0%, rgba(0,234,255,0.13) 71%, rgba(1,12,16,0) 100%);
|
||||
}
|
||||
&__item {
|
||||
color: #409eff;
|
||||
&:hover,&.selcted,&.is-hovering {
|
||||
background-color: rgba(0,234,255,0.13);
|
||||
color: $text__color;
|
||||
font-weight: 700;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&__pagination {
|
||||
.el-input__wrapper {
|
||||
background-color: $dp-primary-color;
|
||||
.el-input__inner {
|
||||
color: $text__color;
|
||||
}
|
||||
}
|
||||
.el-pagination.is-background .btn-next,
|
||||
.el-pagination.is-background .btn-prev,
|
||||
.el-pagination.is-background .el-pager li {
|
||||
background: $pager__bg-color;
|
||||
}
|
||||
.el-pagination.is-background .btn-next.is-active,
|
||||
.el-pagination.is-background .btn-prev.is-active,
|
||||
.el-pagination.is-background .el-pager li.is-active {
|
||||
background-color: $pager__bg-color-isActive;
|
||||
}
|
||||
.btn-prev, .btn-next, .el-pager li {
|
||||
color: #FFF;
|
||||
}
|
||||
.el-pagination {
|
||||
&__total,&__jump {
|
||||
color: $text__color;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__map-select {
|
||||
// 下拉气泡三角颜色
|
||||
&[data-popper-placement^=bottom] .el-popper__arrow::before {
|
||||
background: #00B4FF;
|
||||
}
|
||||
|
||||
.el-select__wrapper {
|
||||
background: linear-gradient(0deg, rgba(0,234,255,0.13) 0%, rgba(1,12,16,0) 100%);
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
&:hover{
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.el-select__suffix {
|
||||
.el-select__caret {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
.el-select__placeholder {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
&.is-transparent {
|
||||
// color: rgb(244, 244, 244, .5);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-light {
|
||||
border-color: #00a2ff;
|
||||
background-color: rgba(0, 120, 233, 0.63) ; // 外层下拉框背景
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
&__wrap {
|
||||
border: none;
|
||||
background-color: #024489; // 下拉框背景色
|
||||
}
|
||||
&__item {
|
||||
color: #409eff;
|
||||
&:hover,&.selcted,&.is-hovering {
|
||||
background-color: #0257A9;
|
||||
color: $text__color;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-cascader-menu {
|
||||
color: #409eff;
|
||||
}
|
||||
.el-cascader-node:not(.is-disabled):focus,
|
||||
.el-cascader-node:not(.is-disabled):hover {
|
||||
background-color: rgba(0,234,255,0.13);
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
// 级联
|
||||
&__map-cascader {
|
||||
background-color: #024489 !important;
|
||||
.el-cascader-node.in-active-path, .el-cascader-node.is-active, .el-cascader-node.is-selectable.in-checked-path {
|
||||
background-color: #0257a9;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
&__dropdown {
|
||||
&.is-light {
|
||||
border-color: #00a2ff;
|
||||
background-color: rgba(0, 120, 233, 0.63) ; // 外层下拉框背景
|
||||
}
|
||||
.el-dropdown-menu {
|
||||
background-color: #024489;
|
||||
&__item {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
.el-dropdown-menu__item:not(.is-disabled):focus,
|
||||
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
background-color: rgba(0,234,255,0.13);
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
}
|
||||
.el-popper.is-light .el-popper__arrow:before {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.el-form {
|
||||
&-item__label {
|
||||
color: #FFF;
|
||||
}
|
||||
.el-input {
|
||||
&__wrapper {
|
||||
background-color: $dp-primary-color;
|
||||
// box-shadow: 0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset;
|
||||
input {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
&-group__append, &-group__prepend {
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__descriptions{
|
||||
font-size: calculate.px2font(16px);
|
||||
color: #FFF;
|
||||
.el-descriptions__header {
|
||||
margin-bottom: calculate.vh(15px);
|
||||
}
|
||||
.el-descriptions__body {
|
||||
background-color: #325b81;
|
||||
.el-descriptions__table.is-bordered .el-descriptions__cell {
|
||||
padding: calculate.vh(12px) calculate.vw(15px);
|
||||
line-height: calculate.vh(25px);
|
||||
}
|
||||
.el-descriptions__content.el-descriptions__cell.is-bordered-content {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
background-color: #195590;
|
||||
}
|
||||
.el-descriptions__label.el-descriptions__cell.is-bordered-label {
|
||||
width: calculate.vw(150px);
|
||||
font-size: inherit;
|
||||
background-color: #155ea2;
|
||||
color: #98C8FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.w100 {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,521 @@
|
|||
<template>
|
||||
<div class="largeScreen">
|
||||
<el-dialog
|
||||
class="largeScreen__dialog"
|
||||
v-model="visible"
|
||||
:width="width"
|
||||
:show-close="false"
|
||||
append-to-body
|
||||
@opened="openedHandle"
|
||||
@close="closeHandle"
|
||||
>
|
||||
<div class="largeScreen__dialog-content" :style="{ '--height--' : height }">
|
||||
<div class="header">
|
||||
<div class="header-title">
|
||||
<el-icon size="16"><CopyDocument /></el-icon>
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
<div class="header-close" @click="closeHandle">
|
||||
<el-icon size="24"><Close /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- v-if="contentShow" -->
|
||||
<div class="container">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div></div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, reactive, onMounted, computed, getCurrentInstance} from "vue";
|
||||
import { fitChartSize, fitChartSizeVh } from "@/utils/styleUtils";
|
||||
import styleUtil from "@/utils/styleUtils";
|
||||
const props = defineProps({
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
width: {
|
||||
type: String || Number,
|
||||
default: styleUtil.px2vw(900),
|
||||
},
|
||||
height: {
|
||||
type: String || Number,
|
||||
default: styleUtil.px2vh(700),
|
||||
},
|
||||
title: {
|
||||
required: true,
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
const contentShow = ref(false);
|
||||
const visible = computed(() => props.open);
|
||||
|
||||
function openedHandle() {
|
||||
contentShow.value = true;
|
||||
}
|
||||
|
||||
// 添加emit事件
|
||||
const emit = defineEmits(["update:open"]);
|
||||
|
||||
// 确定
|
||||
const closeHandle = () => {
|
||||
contentShow.value = false;
|
||||
emit("update:open", false);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
contentShow,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
// 大屏样式
|
||||
$shadow-color: #127BDC;
|
||||
$dialog__title-color: #00f4f5;
|
||||
$dp-primary-color: #00214E;
|
||||
$text__color: #F4F4F4;
|
||||
$pager__bg-color: linear-gradient(rgba(0, 240, 255, 0.1), rgba(13, 172, 255, 0.1));
|
||||
$pager__bg-color-isActive: #127BDC;
|
||||
$header__height: calculate.vh(50px);
|
||||
.largeScreen {
|
||||
// 大屏弹框
|
||||
&__dialog {
|
||||
background-color: $dp-primary-color;
|
||||
padding: calculate.vh(20px) calculate.vh(30px);
|
||||
border-radius: calculate.px2font(15px);
|
||||
box-shadow: inset 0 0 calculate.px2font(20px) $shadow-color; /* 向内阴影,颜色为深红色 */
|
||||
max-height: fit-content;
|
||||
|
||||
.el-dialog__header,.el-dialog__footer {
|
||||
display: none;
|
||||
}
|
||||
.show-close {
|
||||
display: none;
|
||||
}
|
||||
&-content {
|
||||
width: 100%;
|
||||
// height: var(--height--);
|
||||
.header {
|
||||
position: relative;
|
||||
border-bottom: calculate.px2font(2px) solid #072a52;
|
||||
margin-bottom: calculate.px2font(15px);
|
||||
&-title {
|
||||
height: $header__height;
|
||||
color: $dialog__title-color;
|
||||
font-size: calculate.px2font(16px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calculate.px2font(10px);
|
||||
|
||||
.el-icon {
|
||||
font-size: calculate.px2font(20px) !important;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
&-close {
|
||||
position: absolute;
|
||||
top: calculate.vh(10px);
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
border-radius: calculate.px2font(3px);
|
||||
padding: calculate.vh(3px) calculate.vw(5px);
|
||||
background-color: #0c3573;
|
||||
color: #aaa;
|
||||
width: calculate.vh(30px);
|
||||
height: calculate.vh(30px);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
font-size: calculate.px2font(16px) !important;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.container {
|
||||
height: calc(#{var(--height--)} - calculate.vh(60px));
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
.tableWrap {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.el-dialog__body {
|
||||
padding: 0;
|
||||
overflow-y: visible;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
.el-scrollbar__bar {
|
||||
border-radius: calculate.px2font(9999px);
|
||||
height: calculate.vh(10px);
|
||||
}
|
||||
|
||||
.el-card {
|
||||
.el-card__body {
|
||||
--el-font-size-base: #{calculate.px2font(14px)};
|
||||
padding: calculate.vh(14px) calculate.vw(20px) calculate.vh(20px) calculate.vw(20px) !important;
|
||||
|
||||
h4,p {
|
||||
margin: calculate.vh(14px) 0;
|
||||
font-size: calculate.px2font(16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-timeline {
|
||||
.el-timeline-item {
|
||||
.el-timeline-item__node--normal {
|
||||
--el-timeline-node-size-normal: #{calculate.px2font(12px)};
|
||||
}
|
||||
.el-timeline-item__tail {
|
||||
left: calculate.px2font(4px);
|
||||
border-left-width: calculate.px2font(2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-table {
|
||||
--el-table-header-bg-color: #1A87D2;
|
||||
--el-table-bg-color: #29689E;
|
||||
--el-table-border-color: transparent;
|
||||
--el-table-border: 0px solid #043576;
|
||||
--el-table-text-color: #FFF;
|
||||
// --el-table-header-text-color: #98C8FF;
|
||||
--el-table-tr-bg-color: #325b81;
|
||||
--el-table-row-hover-bg-color: #0dacff4d;
|
||||
|
||||
$tableBg: #29689E;
|
||||
|
||||
height: calc(100% - calculate.vh(70px)) !important;
|
||||
font-size: calculate.px2font(14px);
|
||||
|
||||
border-radius: calculate.px2font(10px) calculate.px2font(10px) 0 0;
|
||||
|
||||
.el-table__inner-wrapper {
|
||||
height: 100% !important;
|
||||
background: $tableBg !important;
|
||||
}
|
||||
.el-table__header-wrapper th,
|
||||
.el-table__fixed-header-wrapper th {
|
||||
font-size: calculate.px2font(16px) !important;
|
||||
height: calculate.vh(30px) !important;
|
||||
padding: calculate.vh(10px) calculate.vh(12px);
|
||||
background-color: var(--el-table-header-bg-color) !important;
|
||||
color: var(--el-table-header-text-color);
|
||||
}
|
||||
.cell{
|
||||
padding: 0 calculate.vw(10px);
|
||||
line-height: calculate.vh(23px);
|
||||
}
|
||||
.el-table__cell{
|
||||
padding: calculate.vh(15px) 0;
|
||||
}
|
||||
.el-table__empty-block {
|
||||
height: 100%;
|
||||
}
|
||||
tr {
|
||||
background-color: $tableBg;
|
||||
&:hover {
|
||||
background-color: $tableBg;
|
||||
}
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #3C93D2;
|
||||
&:hover {
|
||||
background-color: #3C93D2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: calculate.vh(20px) 0 0 0;
|
||||
.el-pagination {
|
||||
$buttonWH: calculate.vh(40px);
|
||||
--el-pagination-font-size: #{calculate.px2font(14px)};
|
||||
--el-pagination-button-width: #{$buttonWH};
|
||||
--el-pagination-button-height: #{$buttonWH};
|
||||
--el-pagination-border-radius: #{calculate.px2font(3px)};
|
||||
--el-input-border-radius: #{calculate.px2font(5px)};
|
||||
--el-input-inner-height: calc(#{calculate.vh(32px)} - #{calculate.vh(2px)});
|
||||
--el-pagination-item-gap: #{calculate.px2font(12px)};
|
||||
|
||||
margin-right: calculate.vw(10px);
|
||||
|
||||
button {
|
||||
// border-radius: var(--el-pagination-border-radius) !important;
|
||||
padding: 0 calculate.vh(4px);
|
||||
.el-icon {
|
||||
font-size: calculate.px2font(18px);
|
||||
}
|
||||
}
|
||||
&.is-background .el-pager li.is-active {
|
||||
border-radius: var(--el-pagination-border-radius);
|
||||
}
|
||||
&.is-background {
|
||||
.btn-next, .btn-prev, .el-pager li {
|
||||
margin: 0 calculate.vw(4px);
|
||||
}
|
||||
}
|
||||
&__jump {
|
||||
.el-pagination__editor.el-input {
|
||||
width: calculate.vw(56px);
|
||||
}
|
||||
.el-input__wrapper {
|
||||
padding: calculate.vh(1px) calculate.vh(11px);
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) var(--el-input-border-color,var(--el-border-color)) inset;
|
||||
border-radius: var(--el-pagination-border-radius);
|
||||
.el-input__inner {
|
||||
height: calculate.vh(35px);
|
||||
font-size: calculate.px2font(14px);
|
||||
border-radius: var(--el-pagination-border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-select {
|
||||
width: calculate.vw(128px);
|
||||
.el-select__wrapper {
|
||||
min-height: calculate.vh(40px);
|
||||
line-height: calculate.vh(24px);
|
||||
font-size: calculate.px2font(14px);
|
||||
padding: calculate.vh(4px) calculate.vh(12px);
|
||||
border-radius: calculate.px2font(4px);
|
||||
}
|
||||
.el-select__suffix {
|
||||
.el-icon {
|
||||
width: calculate.vw(20px);
|
||||
height: calculate.vh(20px);
|
||||
line-height: calculate.vh(20px);
|
||||
font-size: calculate.px2font(14px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
&__select, &__pagination {
|
||||
// 下拉气泡三角颜色
|
||||
&[data-popper-placement^=bottom] .el-popper__arrow::before {
|
||||
background: #254277;
|
||||
border: calculate.px2font(1px) solid #254277;
|
||||
}
|
||||
|
||||
.el-select__wrapper {
|
||||
background-color: $dp-primary-color; // 外层下拉框背景
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) #254277 inset;
|
||||
|
||||
&:hover{
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) $shadow-color inset;
|
||||
}
|
||||
}
|
||||
.el-select__placeholder {
|
||||
color: $text__color;
|
||||
&.is-transparent {
|
||||
// color: rgb(244, 244, 244, .5);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-light {
|
||||
border-color: #00a2ff;
|
||||
background-color: rgba(0, 120, 233, 0.63) ; // 外层下拉框背景
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
&__wrap {
|
||||
border: none;
|
||||
// background-color: $dp-primary-color; // 下拉框背景色
|
||||
background-color: linear-gradient(0deg, #00B4FF 0%, rgba(0,234,255,0.13) 71%, rgba(1,12,16,0) 100%);
|
||||
}
|
||||
&__item {
|
||||
color: #409eff;
|
||||
&:hover,&.selcted,&.is-hovering {
|
||||
background-color: rgba(0,234,255,0.13);
|
||||
color: $text__color;
|
||||
font-weight: 700;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&__pagination {
|
||||
.el-input__wrapper {
|
||||
background-color: $dp-primary-color;
|
||||
.el-input__inner {
|
||||
color: $text__color;
|
||||
}
|
||||
}
|
||||
.el-pagination.is-background .btn-next,
|
||||
.el-pagination.is-background .btn-prev,
|
||||
.el-pagination.is-background .el-pager li {
|
||||
background: $pager__bg-color;
|
||||
}
|
||||
.el-pagination.is-background .btn-next.is-active,
|
||||
.el-pagination.is-background .btn-prev.is-active,
|
||||
.el-pagination.is-background .el-pager li.is-active {
|
||||
background-color: $pager__bg-color-isActive;
|
||||
}
|
||||
.btn-prev, .btn-next, .el-pager li {
|
||||
color: #FFF;
|
||||
}
|
||||
.el-pagination {
|
||||
&__total,&__jump {
|
||||
color: $text__color;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__map-select {
|
||||
// 下拉气泡三角颜色
|
||||
&[data-popper-placement^=bottom] .el-popper__arrow::before {
|
||||
background: #00B4FF;
|
||||
}
|
||||
|
||||
.el-select__wrapper {
|
||||
background: linear-gradient(0deg, rgba(0,234,255,0.13) 0%, rgba(1,12,16,0) 100%);
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
&:hover{
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.el-select__suffix {
|
||||
.el-select__caret {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
.el-select__placeholder {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
&.is-transparent {
|
||||
// color: rgb(244, 244, 244, .5);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-light {
|
||||
border-color: #00a2ff;
|
||||
background-color: rgba(0, 120, 233, 0.63) ; // 外层下拉框背景
|
||||
}
|
||||
|
||||
.el-select-dropdown {
|
||||
&__wrap {
|
||||
border: none;
|
||||
background-color: #024489; // 下拉框背景色
|
||||
}
|
||||
&__item {
|
||||
color: #409eff;
|
||||
&:hover,&.selcted,&.is-hovering {
|
||||
background-color: #0257A9;
|
||||
color: $text__color;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-cascader-menu {
|
||||
color: #409eff;
|
||||
}
|
||||
.el-cascader-node:not(.is-disabled):focus,
|
||||
.el-cascader-node:not(.is-disabled):hover {
|
||||
background-color: rgba(0,234,255,0.13);
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
// 级联
|
||||
&__map-cascader {
|
||||
background-color: #024489 !important;
|
||||
.el-cascader-node.in-active-path, .el-cascader-node.is-active, .el-cascader-node.is-selectable.in-checked-path {
|
||||
background-color: #0257a9;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
&__dropdown {
|
||||
&.is-light {
|
||||
border-color: #00a2ff;
|
||||
background-color: rgba(0, 120, 233, 0.63) ; // 外层下拉框背景
|
||||
}
|
||||
.el-dropdown-menu {
|
||||
background-color: #024489;
|
||||
&__item {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
.el-dropdown-menu__item:not(.is-disabled):focus,
|
||||
.el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
background-color: rgba(0,234,255,0.13);
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
}
|
||||
.el-popper.is-light .el-popper__arrow:before {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.el-form {
|
||||
&-item__label {
|
||||
color: #FFF;
|
||||
}
|
||||
.el-input {
|
||||
&__wrapper {
|
||||
background-color: $dp-primary-color;
|
||||
// box-shadow: 0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset;
|
||||
input {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
&-group__append, &-group__prepend {
|
||||
background-color: #409EFF;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__descriptions{
|
||||
font-size: calculate.px2font(16px);
|
||||
color: #FFF;
|
||||
.el-descriptions__header {
|
||||
margin-bottom: calculate.vh(15px);
|
||||
}
|
||||
.el-descriptions__body {
|
||||
background-color: #325b81;
|
||||
.el-descriptions__table.is-bordered .el-descriptions__cell {
|
||||
padding: calculate.vh(12px) calculate.vw(15px);
|
||||
line-height: calculate.vh(25px);
|
||||
}
|
||||
.el-descriptions__content.el-descriptions__cell.is-bordered-content {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
background-color: #195590;
|
||||
}
|
||||
.el-descriptions__label.el-descriptions__cell.is-bordered-label {
|
||||
width: calculate.vw(150px);
|
||||
font-size: inherit;
|
||||
background-color: #155ea2;
|
||||
color: #98C8FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.w100 {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
|||
/* src/assets/css/leaflet.css */
|
||||
@import 'leaflet/dist/leaflet.css';
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,427 @@
|
|||
<template>
|
||||
<div class="perfect-time-slider">
|
||||
<!-- 日期选择器 -->
|
||||
<div class="date-picker">
|
||||
<el-date-picker
|
||||
v-model="selectedDate"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
@change="handleDateChange"
|
||||
:disabled-date="disabledDate"
|
||||
placeholder="选择日期:"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
|
||||
<!-- 时间滑块 -->
|
||||
<div class="slider-container">
|
||||
<div class="slider-header">
|
||||
<span class="current-time">{{ formattedTime }}</span>
|
||||
<button @click="togglePlay" class="play-button">
|
||||
{{ isPlaying ? "暂停" : "播放" }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 带刻度的时间滑块 -->
|
||||
<div class="slider-wrapper">
|
||||
<div class="slider-track" :style="trackStyle">
|
||||
<input
|
||||
type="range"
|
||||
v-model="sliderValue"
|
||||
:min="0"
|
||||
:max="maxSliderMinutes"
|
||||
class="slider"
|
||||
@input="handleSliderChange"
|
||||
step="1"
|
||||
:disabled="isFutureDate"
|
||||
/>
|
||||
<div class="progress" :style="progressStyle"></div>
|
||||
</div>
|
||||
|
||||
<!-- 刻度标记 -->
|
||||
<div class="ticks" :style="ticksContainerStyle">
|
||||
<span
|
||||
v-for="(tick, index) in ticks"
|
||||
:key="index"
|
||||
:style="{ left: tick.position + '%' }"
|
||||
class="tick"
|
||||
>
|
||||
<span class="tick-label">{{ tick.label }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
<div v-if="isFutureDate" class="future-date-message">
|
||||
已到达当前时间,请选择更早的日期
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
|
||||
import { getTimeLine } from "@/api/map.js";
|
||||
|
||||
const props = defineProps({
|
||||
playSpeed: {
|
||||
type: Number,
|
||||
default: 3000,
|
||||
},
|
||||
slideHours: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["timeChange"]);
|
||||
|
||||
// 状态管理
|
||||
const isPlaying = ref(false);
|
||||
const sliderValue = ref(0);
|
||||
const playInterval = ref(null);
|
||||
const selectedDate = ref(getFormattedDate(new Date()));
|
||||
const currentTime = ref(new Date());
|
||||
const now = ref(new Date());
|
||||
const canAutoPlay = ref(false); // 新增:控制是否允许自动播放
|
||||
// 计算属性
|
||||
const maxSliderMinutes = computed(() => {
|
||||
if (isToday.value) {
|
||||
return now.value.getHours() * 60 + now.value.getMinutes();
|
||||
}
|
||||
return 24 * 60;
|
||||
});
|
||||
|
||||
const trackStyle = computed(() => ({
|
||||
width: `${(maxSliderMinutes.value / (24 * 60)) * 100}%`,
|
||||
}));
|
||||
|
||||
const ticksContainerStyle = computed(() => ({
|
||||
width: `${(maxSliderMinutes.value / (24 * 60)) * 100}%`,
|
||||
}));
|
||||
|
||||
const progressStyle = computed(() => ({
|
||||
width: `${(sliderValue.value / maxSliderMinutes.value) * 100}%`,
|
||||
}));
|
||||
|
||||
const formattedTime = computed(() => formatDateTime(currentTime.value));
|
||||
|
||||
const ticks = computed(() => {
|
||||
const ticks = [];
|
||||
const totalMinutes = maxSliderMinutes.value;
|
||||
const hourSteps = isToday.value ? 2 : 2; // 每2小时一个刻度
|
||||
|
||||
for (let hour = 0; hour <= 24; hour += hourSteps) {
|
||||
const minutes = hour * 60;
|
||||
if (minutes > totalMinutes) break;
|
||||
|
||||
ticks.push({
|
||||
position: (minutes / totalMinutes) * 100,
|
||||
label: `${hour.toString().padStart(2, "0")}:00`,
|
||||
});
|
||||
}
|
||||
return ticks;
|
||||
});
|
||||
|
||||
const isToday = computed(() => {
|
||||
return selectedDate.value === getFormattedDate(new Date());
|
||||
});
|
||||
|
||||
const isFutureDate = computed(() => {
|
||||
return new Date(selectedDate.value) > new Date(getFormattedDate(new Date()));
|
||||
});
|
||||
|
||||
const maxSelectableDate = computed(() => {
|
||||
return getFormattedDate(new Date());
|
||||
});
|
||||
|
||||
// 日期变化处理
|
||||
const handleDateChange = () => {
|
||||
const [year, month, day] = selectedDate.value.split("-");
|
||||
const newDate = new Date(year, month - 1, day, 0, 0, 0);
|
||||
updateTime(newDate);
|
||||
sliderValue.value = 0;
|
||||
canAutoPlay.value = true; // 日期改变后允许自动播放
|
||||
|
||||
if (isFutureDate.value && isPlaying.value) {
|
||||
togglePlay();
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化日期为YYYY-MM-DD
|
||||
function getFormattedDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||
const day = date.getDate().toString().padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
const formatDateTime = (date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const hours = date.getHours().toString().padStart(2, "0");
|
||||
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||||
const seconds = date.getSeconds().toString().padStart(2, "0");
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
// 处理滑块变化
|
||||
const handleSliderChange = () => {
|
||||
const hours = Math.floor(sliderValue.value / 60);
|
||||
const minutes = sliderValue.value % 60;
|
||||
const newTime = new Date(selectedDate.value);
|
||||
newTime.setHours(hours, minutes);
|
||||
|
||||
// 如果是今天,确保时间不超过当前时间
|
||||
if (isToday.value && newTime > now.value) {
|
||||
newTime.setTime(now.value.getTime());
|
||||
updateSliderPosition(newTime);
|
||||
}
|
||||
|
||||
updateTime(newTime);
|
||||
canAutoPlay.value = true; // 滑块拖动后允许自动播放
|
||||
};
|
||||
|
||||
// 更新时间
|
||||
const updateTime = (newTime) => {
|
||||
currentTime.value = newTime;
|
||||
console.log(formatDateTime(newTime));
|
||||
localStorage.setItem("timeValue", formatDateTime(newTime));
|
||||
emit("timeChange", newTime);
|
||||
};
|
||||
|
||||
// 滑动到下一个时间点
|
||||
const slideToNext = () => {
|
||||
const newTime = new Date(currentTime.value);
|
||||
newTime.setHours(newTime.getHours() + props.slideHours);
|
||||
|
||||
// 检查是否超过了当前时间(如果是今天)
|
||||
if (isToday.value && newTime > now.value) {
|
||||
// 到达今天当前时间,停止播放
|
||||
if (isPlaying.value) {
|
||||
togglePlay();
|
||||
}
|
||||
canAutoPlay.value = false; // 播放完成后禁止自动播放
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否超过了当天的23:59:59(对于非今天日期)
|
||||
if (!isToday.value && newTime.getDate() !== currentTime.value.getDate()) {
|
||||
// 到达当天结束时间,停止播放
|
||||
if (isPlaying.value) {
|
||||
togglePlay();
|
||||
}
|
||||
canAutoPlay.value = false; // 播放完成后禁止自动播放
|
||||
return;
|
||||
}
|
||||
|
||||
updateTime(newTime);
|
||||
updateSliderPosition(newTime);
|
||||
};
|
||||
|
||||
function disabledDate(time) {
|
||||
// 获取今天的日期(不包含时间)
|
||||
const today = new Date().setHours(0, 0, 0, 0);
|
||||
// 将传入的日期时间转换为不包含时间的格式进行比较
|
||||
const dateTime = time.getTime();
|
||||
// 如果传入的日期大于今天,则禁用
|
||||
return dateTime > today;
|
||||
}
|
||||
// 更新滑块位置
|
||||
const updateSliderPosition = (time) => {
|
||||
sliderValue.value = time.getHours() * 60 + time.getMinutes();
|
||||
};
|
||||
|
||||
// 切换播放状态
|
||||
const togglePlay = () => {
|
||||
// 移除初始的 canAutoPlay 检查,允许第一次点击播放
|
||||
isPlaying.value = !isPlaying.value;
|
||||
if (isPlaying.value) {
|
||||
playInterval.value = setInterval(slideToNext, props.playSpeed);
|
||||
} else {
|
||||
clearInterval(playInterval.value);
|
||||
playInterval.value = null;
|
||||
}
|
||||
};
|
||||
// 每分钟更新当前时间
|
||||
const updateNow = () => {
|
||||
now.value = new Date();
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
const startOfDay = new Date(selectedDate.value);
|
||||
startOfDay.setHours(0, 0, 0, 0);
|
||||
updateTime(startOfDay);
|
||||
updateSliderPosition(startOfDay);
|
||||
setInterval(updateNow, 60000);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (playInterval.value) {
|
||||
clearInterval(playInterval.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.perfect-time-slider {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 6%;
|
||||
font-family: Arial, sans-serif;
|
||||
background-image: url("../../../assets/images/timeline.png");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.date-picker {
|
||||
margin-bottom: 4px;
|
||||
::v-deep .el-input__wrapper {
|
||||
background: #ebf5ff;
|
||||
}
|
||||
}
|
||||
|
||||
.date-picker label {
|
||||
margin-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.date-picker input {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.slider-container {
|
||||
height: 100px;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slider-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.current-time {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
color: #ebf5ff;
|
||||
}
|
||||
|
||||
.play-button {
|
||||
padding: 8px 20px;
|
||||
background-color: #ebf5ff;
|
||||
color: #409eff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
transition: background-color 0.3s;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.play-button:hover {
|
||||
background-color: #ebf5ff;
|
||||
}
|
||||
|
||||
.slider-wrapper {
|
||||
position: relative;
|
||||
// margin: 40px 0 20px;
|
||||
}
|
||||
|
||||
.slider-track {
|
||||
position: relative;
|
||||
height: 10px;
|
||||
background: #4a5d79;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
z-index: 3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
background: #4a5d79;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #0b3054;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #4a5d79;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
z-index: 4;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slider:disabled::-webkit-slider-thumb {
|
||||
background: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #4caf50;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.ticks {
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.tick {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
background: #fff;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.tick-label {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0.8em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.future-date-message {
|
||||
margin-top: 10px;
|
||||
color: #ff5722;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
|
@ -0,0 +1,473 @@
|
|||
<template>
|
||||
<div class="monitor-container">
|
||||
<!-- 分屏控制 -->
|
||||
<div class="control-bar">
|
||||
<el-tag
|
||||
class="tags"
|
||||
v-for="item in viewModes"
|
||||
:key="item.value"
|
||||
size="large"
|
||||
:effect="activeMode === item.value ? 'dark' : ''"
|
||||
:type="activeMode === item.value ? 'primary' : ''"
|
||||
@click="handleViewModeChange(item.value)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-tag>
|
||||
<!-- <el-select
|
||||
v-model="selectValue"
|
||||
filterable
|
||||
placeholder="请选择部门"
|
||||
class="dept-select custam-select"
|
||||
popper-class="custam-select-pupper"
|
||||
@change="handleDeptChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in bmList"
|
||||
:key="item.deptid"
|
||||
:label="item.deptname"
|
||||
:value="item.deptid"
|
||||
/>
|
||||
</el-select> -->
|
||||
<el-select
|
||||
v-model="selectedCameraCodes"
|
||||
multiple
|
||||
filterable
|
||||
placeholder="请选择监控点"
|
||||
class="camera-select custam-select"
|
||||
:disabled="!selectValue"
|
||||
@change="handleCameraChange"
|
||||
style="width: 100%"
|
||||
popper-class="custam-select-pupper"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in availableCameras"
|
||||
:key="item.code"
|
||||
:label="item.name"
|
||||
:value="item.code"
|
||||
@click.stop="handleOptionClick(item)"
|
||||
>
|
||||
<span :class="{ 'no-stream': !item.depturl }">
|
||||
{{ item.name }}
|
||||
<span
|
||||
v-if="!item.depturl && loadingCameras.includes(item.code)"
|
||||
class="loading-text"
|
||||
>
|
||||
(加载中...)
|
||||
</span>
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 播放器容器 -->
|
||||
<div id="player" :data-mode="activeMode"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount, computed, watch } from "vue";
|
||||
import {
|
||||
getDeptOfWwsList,
|
||||
getDeptOfWwsUrlList,
|
||||
getvideoWws,
|
||||
} from "@/api/camera.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const viewModes = [
|
||||
{ value: 4, label: "四组" },
|
||||
// { value: 9, label: "九组" },
|
||||
];
|
||||
|
||||
const activeMode = ref(4);
|
||||
const currentType = ref(0);
|
||||
let player = null;
|
||||
|
||||
const videoUrls = ref([]);
|
||||
const cameraList = ref([]);
|
||||
const selectedCameraCodes = ref([]);
|
||||
const loadingCameras = ref([]);
|
||||
|
||||
const bmList = ref([]);
|
||||
const selectValue = ref("");
|
||||
|
||||
// 可用的监控点列表
|
||||
const availableCameras = computed(() => {
|
||||
return cameraList.value.filter((item) => item.deptid === selectValue.value);
|
||||
});
|
||||
|
||||
// 处理选项点击
|
||||
const handleOptionClick = async (camera) => {
|
||||
// 如果已经有视频流或者是正在加载,不处理
|
||||
if (camera.depturl || loadingCameras.value.includes(camera.code)) return;
|
||||
|
||||
// 检查是否已经选中
|
||||
const isSelected = selectedCameraCodes.value.includes(camera.code);
|
||||
|
||||
// 如果未选中且已达到上限,提示并不调用接口
|
||||
if (!isSelected && selectedCameraCodes.value.length >= activeMode.value) {
|
||||
ElMessage.warning(`最多只能选择${activeMode.value}个监控点`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loadingCameras.value.push(camera.code);
|
||||
|
||||
const res = await getvideoWws({
|
||||
typeinfo: camera.filePath || camera.code,
|
||||
});
|
||||
|
||||
const depturl = res.msg;
|
||||
|
||||
if (!depturl) {
|
||||
throw new Error("接口返回的视频流URL为空");
|
||||
}
|
||||
|
||||
// 更新监控点数据
|
||||
const index = cameraList.value.findIndex(
|
||||
(item) => item.code === camera.code
|
||||
);
|
||||
if (index !== -1) {
|
||||
cameraList.value[index].depturl = depturl;
|
||||
|
||||
// 如果未选中,则添加到选中列表
|
||||
if (!isSelected) {
|
||||
selectedCameraCodes.value = [...selectedCameraCodes.value, camera.code];
|
||||
}
|
||||
|
||||
initPlayer();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取视频流失败:", error);
|
||||
ElMessage.error(`获取视频流失败: ${error.message}`);
|
||||
} finally {
|
||||
loadingCameras.value = loadingCameras.value.filter(
|
||||
(item) => item !== camera.code
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 监控点选择变化
|
||||
const handleCameraChange = (selectedCodes) => {
|
||||
// 去重处理
|
||||
const uniqueCodes = [...new Set(selectedCodes)];
|
||||
const maxSelect = activeMode.value;
|
||||
|
||||
if (uniqueCodes.length > maxSelect) {
|
||||
selectedCameraCodes.value = uniqueCodes.slice(0, maxSelect);
|
||||
ElMessage.warning(`最多只能选择${maxSelect}个监控点`);
|
||||
return;
|
||||
}
|
||||
|
||||
selectedCameraCodes.value = uniqueCodes;
|
||||
initPlayer();
|
||||
};
|
||||
|
||||
// 获取监控点数据
|
||||
const fetchCameraList = async () => {
|
||||
if (!selectValue.value) return;
|
||||
|
||||
const par = {
|
||||
deptid: selectValue.value,
|
||||
type: currentType.value,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await getDeptOfWwsUrlList(par);
|
||||
cameraList.value = res.rows.map((item) => ({
|
||||
...item,
|
||||
filePath: item.filePath || item.code,
|
||||
}));
|
||||
|
||||
// 默认选择前N个有视频流的监控点
|
||||
const maxSelect = activeMode.value;
|
||||
const validCameras = cameraList.value.filter((item) => item.depturl);
|
||||
selectedCameraCodes.value = validCameras
|
||||
.slice(0, maxSelect)
|
||||
.map((item) => item.code);
|
||||
|
||||
initPlayer();
|
||||
} catch (error) {
|
||||
console.error("获取监控点列表失败:", error);
|
||||
ElMessage.error("获取监控点列表失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 部门变更处理
|
||||
const handleDeptChange = () => {
|
||||
selectedCameraCodes.value = [];
|
||||
fetchCameraList();
|
||||
};
|
||||
|
||||
// 分屏模式切换处理
|
||||
const handleViewModeChange = async (mode) => {
|
||||
activeMode.value = mode;
|
||||
currentType.value = mode === 9 ? 1 : 0;
|
||||
|
||||
// 调整已选数量
|
||||
if (selectedCameraCodes.value.length > mode) {
|
||||
selectedCameraCodes.value = selectedCameraCodes.value.slice(0, mode);
|
||||
}
|
||||
|
||||
await fetchCameraList();
|
||||
initPlayer();
|
||||
};
|
||||
|
||||
// 初始化播放器
|
||||
const initPlayer = () => {
|
||||
try {
|
||||
// 只播放有有效视频流的监控点
|
||||
const validCameras = availableCameras.value.filter(
|
||||
(item) => selectedCameraCodes.value.includes(item.code) && item.depturl
|
||||
);
|
||||
const urls = validCameras.map((item) => item.depturl);
|
||||
|
||||
if (urls.length === 0) {
|
||||
console.log("没有可播放的视频流");
|
||||
if (player) {
|
||||
player.JS_StopRealPlayAll();
|
||||
player.JS_Destroy();
|
||||
player = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (player) {
|
||||
player.JS_StopRealPlayAll();
|
||||
player.JS_Destroy();
|
||||
}
|
||||
|
||||
player = new JSPlugin({
|
||||
szId: "player",
|
||||
szBasePath: "js/",
|
||||
iMaxSplit: activeMode.value,
|
||||
openDebug: true,
|
||||
mseWorkerEnable: false,
|
||||
bSupporDoubleClickFull: true,
|
||||
supportGMProtocol: true, // 启用国密协议支持
|
||||
oStyle: {
|
||||
borderSelect: "#343434",
|
||||
},
|
||||
});
|
||||
|
||||
player.JS_ArrangeWindow(activeMode.value);
|
||||
|
||||
const videosToPlay = Math.min(activeMode.value, urls.length);
|
||||
for (let i = 0; i < videosToPlay; i++) {
|
||||
player.JS_Play(
|
||||
urls[i],
|
||||
{
|
||||
playURL: urls[i],
|
||||
mode: 0,
|
||||
keepDecoder: 0,
|
||||
token: "",
|
||||
},
|
||||
i
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("初始化播放器失败:", error);
|
||||
ElMessage.error("播放器初始化失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 获取部门列表
|
||||
const getDeptList = () => {
|
||||
getDeptOfWwsList()
|
||||
.then((res) => {
|
||||
console.log(res, "sdsdasadsa");
|
||||
bmList.value = res.rows;
|
||||
if (bmList.value.length > 0) {
|
||||
selectValue.value = bmList.value[0].deptid;
|
||||
handleDeptChange();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("获取部门列表失败:", error);
|
||||
ElMessage.error("获取部门列表失败");
|
||||
});
|
||||
};
|
||||
|
||||
// 监听选中监控点变化
|
||||
watch(selectedCameraCodes, (newVal) => {
|
||||
console.log("当前选中监控点:", newVal);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getDeptList();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (player) {
|
||||
player.JS_StopRealPlayAll();
|
||||
player.JS_Destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
$inputHeight: calculate.vh(45px);
|
||||
$selectBg: #16437F;
|
||||
:deep(.el-select__wrapper) {
|
||||
width: 100%;
|
||||
height: $inputHeight;
|
||||
overflow: hidden;
|
||||
min-height: $inputHeight;
|
||||
line-height: $inputHeight;
|
||||
/* padding: 6%; */
|
||||
background: transparent;
|
||||
color: #1890ff;
|
||||
background-color: $selectBg;
|
||||
border-radius: calculate.px2font(5px);
|
||||
}
|
||||
.tags {
|
||||
height: $inputHeight;
|
||||
border-radius: calculate.px2font(5px);
|
||||
font-size: calculate.px2font(14px);
|
||||
padding: 0 calculate.vw(10px);
|
||||
border-width: calculate.px2font(1px);
|
||||
}
|
||||
|
||||
.monitor-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.control-bar,
|
||||
.select-container {
|
||||
margin-bottom: calculate.vh(15px);
|
||||
// padding: 10px;
|
||||
display: flex;
|
||||
gap: calculate.vw(15px);
|
||||
// background: #09375b;
|
||||
border-radius: calculate.px2font(4px);
|
||||
box-shadow: 0 calculate.px2font(1px) calculate.px2font(4px) rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.select-container {
|
||||
// padding: 10px;
|
||||
align-items: center;
|
||||
:deep(.el-select__wrapper) {
|
||||
width: 100%;
|
||||
// height: 60px;
|
||||
overflow: hidden;
|
||||
line-height: calculate.vh(40px);
|
||||
/* padding: 6%; */
|
||||
background: $selectBg;
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
.camera-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#player {
|
||||
flex: 1;
|
||||
background: #000;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.no-stream {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
color: #1890ff;
|
||||
font-size: calculate.px2font(12px);
|
||||
margin-left: calculate.vw(4px);
|
||||
}
|
||||
|
||||
/* 四组模式 - 2x2 */
|
||||
#player[data-mode="4"] :deep(> div > div) {
|
||||
width: 50% !important;
|
||||
height: 50% !important;
|
||||
float: left !important;
|
||||
}
|
||||
|
||||
/* 九组模式 - 3x3 */
|
||||
#player[data-mode="9"] :deep(> div > div) {
|
||||
width: 33.333% !important;
|
||||
height: 33.333% !important;
|
||||
float: left !important;
|
||||
}
|
||||
|
||||
/* 确保视频填满每个格子 */
|
||||
#player :deep(> div > div > video) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
/* 下拉框样式 */
|
||||
.camera-select-dropdown {
|
||||
max-height: calculate.vh(400px) !important;
|
||||
overflow-y: auto !important;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #4a6b8b #1e3a5f;
|
||||
background-color: #1e3a5f !important;
|
||||
border: 1px solid #09375b !important;
|
||||
}
|
||||
|
||||
.camera-select-dropdown::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.camera-select-dropdown::-webkit-scrollbar-track {
|
||||
background: #1e3a5f;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.camera-select-dropdown::-webkit-scrollbar-thumb {
|
||||
background-color: #4a6b8b;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.camera-select-dropdown .el-select-dropdown__item {
|
||||
padding: 0 20px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
color: #c9d9f3 !important;
|
||||
background-color: #1e3a5f !important;
|
||||
}
|
||||
|
||||
.camera-select-dropdown .el-select-dropdown__item.hover,
|
||||
.camera-select-dropdown .el-select-dropdown__item:hover {
|
||||
background-color: #2c4a6e !important;
|
||||
}
|
||||
|
||||
.camera-select-dropdown .el-select-dropdown__item.selected {
|
||||
color: #ffffff !important;
|
||||
background-color: #09375b !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 多选框选中标签样式 */
|
||||
.el-select__tags {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
// .el-tag {
|
||||
// background-color: #12457C !important;
|
||||
// border-color: #4a6b8b !important;
|
||||
// color: #c9d9f3 !important;
|
||||
// }
|
||||
|
||||
.el-tag .el-icon-close {
|
||||
color: #c9d9f3 !important;
|
||||
}
|
||||
|
||||
.el-tag .el-icon-close:hover {
|
||||
background-color: #4a6b8b !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,968 @@
|
|||
<template>
|
||||
<div class="wrap">
|
||||
<div class="header">
|
||||
<div class="bgtitle">
|
||||
<!-- <img src="../../assets/logo/dkl.png" alt="logo" /> -->
|
||||
<img src="../../assets/images/largeScreenImage/dp-logo.png" alt="">
|
||||
<span>大客流一张图</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer"></div>
|
||||
<div class="largeScreen__btn">
|
||||
<div class="rights">
|
||||
<router-link to="/index">
|
||||
<img
|
||||
src="../../assets/images/sy.png"
|
||||
alt="首页"
|
||||
class="icon"
|
||||
/>
|
||||
</router-link>
|
||||
<img
|
||||
src="../../assets/images/qp.png"
|
||||
alt="全屏"
|
||||
class="icon hover-effect"
|
||||
@click="toggleFullscreen"
|
||||
/>
|
||||
<router-link to="/index" class="flex">
|
||||
<span>进入系统</span>
|
||||
<img
|
||||
src="../../assets/images/fh.png"
|
||||
alt="返回"
|
||||
class="back"
|
||||
/>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模态框部分 -->
|
||||
<LargeScreenModal v-model:open="chargeOpen" :title="modalTitle">
|
||||
<Analysis v-if="caseTab == '1'"></Analysis>
|
||||
</LargeScreenModal>
|
||||
<LargeScreenModal v-model:open="chargeOpen2" :title="modalTitle">
|
||||
<!-- 内容 -->
|
||||
</LargeScreenModal>
|
||||
<LargeScreenModal v-model:open="chargeOpen3" :title="modalTitle">
|
||||
<RiskAnalysis v-if="caseTab3 == '1'"></RiskAnalysis>
|
||||
</LargeScreenModal>
|
||||
<LargeScreenModal v-model:open="chargeOpen4" :title="modalTitle">
|
||||
<!-- 内容 -->
|
||||
</LargeScreenModal>
|
||||
|
||||
<div class="container">
|
||||
<div class="container__top">
|
||||
<div class="left flexcolumns">
|
||||
<div class="card BarWrap">
|
||||
<div class="card__title flex-btn">
|
||||
<div>大客流总数区域展示</div>
|
||||
</div>
|
||||
<div class="card__content noBg">
|
||||
<div class="titles">大客流总数区域展示</div>
|
||||
<InfoOverview :time="newTime"></InfoOverview>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card BarWrap">
|
||||
<div class="card__title flex-btn">
|
||||
<div>大客流风险预警信息</div>
|
||||
</div>
|
||||
<div class="card__content">
|
||||
<div class="titles">待处理预警信息</div>
|
||||
<!-- 监听EarlyWarning组件的事件 -->
|
||||
<EarlyWarning :time="newTime" @update-map-params="handleMapUpdate" ></EarlyWarning>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="middle">
|
||||
<!-- 地图组件参数绑定 - 添加updateTrigger确保更新 -->
|
||||
<Map
|
||||
:screenId="SYid"
|
||||
:latitude="currentLatitude"
|
||||
:longitude="currentLongitude"
|
||||
:tabName="tabName"
|
||||
:monitoringTypeName="currentMonitoringType"
|
||||
:update-trigger="updateTrigger"
|
||||
></Map>
|
||||
</div>
|
||||
<div class="right flexcolumns">
|
||||
<div class="card BarWrap">
|
||||
<div class="card__title flex-btn">
|
||||
<div>视频监控</div>
|
||||
</div>
|
||||
<div class="card__content noBg">
|
||||
<div class="titles">视频监控</div>
|
||||
<VideoSurveillance></VideoSurveillance>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card BarWrap">
|
||||
<div class="card__title flex-btn">
|
||||
<div>处置状态监控</div>
|
||||
</div>
|
||||
<div class="card__content">
|
||||
<div class="titles">处置状态监控</div>
|
||||
<MonitoringPoints></MonitoringPoints>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, getCurrentInstance, watch } from "vue";
|
||||
import Moment from "moment";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import EnhancedTimeSlider from "./components/timeline.vue";
|
||||
const route = useRoute();
|
||||
const SYid = route.query.screenId;
|
||||
|
||||
// 关键修改:添加强制更新触发器
|
||||
const updateTrigger = ref(0);
|
||||
|
||||
// 路由参数转换为响应式变量
|
||||
const routeLatitude = ref(route.query.latitude);
|
||||
const routeLongitude = ref(route.query.longitude);
|
||||
const tabName = ref(route.query.tabName);
|
||||
const routeMonitoringType = ref(route.query.monitoringTypeName);
|
||||
|
||||
// 响应式地图参数
|
||||
const currentLatitude = ref(routeLatitude.value);
|
||||
const currentLongitude = ref(routeLongitude.value);
|
||||
const currentMonitoringType = ref(routeMonitoringType.value);
|
||||
|
||||
// 格式化时间
|
||||
const formatDateTime = (date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const hours = date.getHours().toString().padStart(2, "0");
|
||||
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||||
const seconds = date.getSeconds().toString().padStart(2, "0");
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
const activeNames = ref(["1"]);
|
||||
const DaystartTime = ref("");
|
||||
const DayendTime = ref("");
|
||||
const newTime = ref("");
|
||||
const handleTimeChange = (timeValue) => {
|
||||
newTime.value = formatDateTime(timeValue);
|
||||
const datePart = newTime.value.split(" ")[0];
|
||||
DaystartTime.value = datePart + " 00:00:00";
|
||||
DayendTime.value = datePart + " 23:59:59";
|
||||
};
|
||||
|
||||
// 导入组件
|
||||
import LargeScreenModal from "./components/largeScreenModal.vue";
|
||||
import InfoOverview from "./components/InfoOverview/index.vue";
|
||||
import Analysis from "./components/InfoOverview/components/analysis.vue";
|
||||
import EarlyWarning from "./components/earlyWarning/index.vue";
|
||||
import RiskAnalysis from "./components/earlyWarning/components/riskAnalysis.vue";
|
||||
import VideoSurveillance from "./components/videoSurveillance/index.vue";
|
||||
import MonitoringPoints from "./components/monitoringPoints/index.vue";
|
||||
import DropdownItem from "./components/dropdownItem.vue";
|
||||
import Map from "./components/map/index.vue";
|
||||
|
||||
const imgList = ref([
|
||||
{ src: new URL("../../assets/images/x2.jpg", import.meta.url).href },
|
||||
{ src: new URL("../../assets/images/x1.jpg", import.meta.url).href },
|
||||
{ src: new URL("../../assets/images/x3.jpg", import.meta.url).href },
|
||||
{ src: new URL("../../assets/images/x4.jpg", import.meta.url).href },
|
||||
{ src: new URL("../../assets/images/x5.jpg", import.meta.url).href },
|
||||
{ src: new URL("../../assets/images/x6.jpg", import.meta.url).href },
|
||||
{ src: new URL("../../assets/images/x8.jpg", import.meta.url).href },
|
||||
]);
|
||||
const { proxy } = getCurrentInstance();
|
||||
let router = useRouter();
|
||||
|
||||
// 模态框状态
|
||||
const chargeOpen = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const chargeOpen2 = ref(false);
|
||||
const chargeOpen3 = ref(false);
|
||||
const chargeOpen4 = ref(false);
|
||||
|
||||
const caseTab = ref("1");
|
||||
const caseTab2 = ref("1");
|
||||
const caseTab3 = ref("1");
|
||||
const caseTab4 = ref("1");
|
||||
|
||||
const caseTab_options = reactive([{ value: "1", label: "大客流信息数据分析" }]);
|
||||
const caseTab2_options = reactive([
|
||||
{ value: "1", label: "日历展示" },
|
||||
{ value: "2", label: "普法学习" },
|
||||
]);
|
||||
const caseTab3_options = reactive([
|
||||
{ value: "1", label: "大客流风险预警分析" },
|
||||
]);
|
||||
const caseTab4_options = reactive([{ value: "1", label: "模型碰撞" }]);
|
||||
|
||||
function getDate(time, type = "YYYY-MM-DD") {
|
||||
let date = time || new Date().getTime();
|
||||
return Moment(date).format(type);
|
||||
}
|
||||
|
||||
// 关键修改:处理EarlyWarning组件传递的参数,增加强制更新
|
||||
const handleMapUpdate = (params) => {
|
||||
// 更新当前地图参数
|
||||
currentLatitude.value = params.latitude;
|
||||
currentLongitude.value = params.longitude;
|
||||
currentMonitoringType.value = params.monitoringTypeName;
|
||||
|
||||
// 关键:每次点击更新触发标记,确保地图组件重新处理
|
||||
updateTrigger.value = Date.now();
|
||||
|
||||
// 更新路由参数
|
||||
router.replace({
|
||||
query: {
|
||||
...route.query,
|
||||
latitude: params.latitude,
|
||||
longitude: params.longitude,
|
||||
monitoringTypeName: params.monitoringTypeName
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 监听路由参数变化
|
||||
watch(
|
||||
() => [route.query.latitude, route.query.longitude, route.query.monitoringTypeName],
|
||||
([lat, lng, type]) => {
|
||||
if (lat && lng) {
|
||||
currentLatitude.value = lat;
|
||||
currentLongitude.value = lng;
|
||||
updateTrigger.value = Date.now(); // 触发更新
|
||||
}
|
||||
if (type) {
|
||||
currentMonitoringType.value = type;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
nowTime.value = getDate();
|
||||
currYearMonth.value = getDate(null, "YYYY-MM");
|
||||
});
|
||||
|
||||
function toPage() {
|
||||
router.push("/");
|
||||
}
|
||||
|
||||
function getDropLabel(arr, val) {
|
||||
let obj = arr.find((v) => v.value === val);
|
||||
return obj ? obj.label : "";
|
||||
}
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen().catch((err) => {
|
||||
console.error(`全屏错误: ${err.message}`);
|
||||
});
|
||||
} else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function dropDownHandle(e, type) {
|
||||
switch (type) {
|
||||
case "caseTab":
|
||||
caseTab.value = e;
|
||||
chargeOpen.value = true;
|
||||
modalTitle.value = getDropLabel(caseTab_options, e);
|
||||
break;
|
||||
case "caseTab2":
|
||||
caseTab2.value = e;
|
||||
chargeOpen2.value = true;
|
||||
modalTitle.value = getDropLabel(caseTab2_options, e);
|
||||
break;
|
||||
case "caseTab3":
|
||||
caseTab3.value = e;
|
||||
chargeOpen3.value = true;
|
||||
modalTitle.value = getDropLabel(caseTab3_options, e);
|
||||
break;
|
||||
case "caseTab4":
|
||||
caseTab4.value = e;
|
||||
chargeOpen4.value = true;
|
||||
modalTitle.value = getDropLabel(caseTab4_options, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 时间相关
|
||||
const initialTime = ref(new Date(2025, 5, 10, 4, 0, 0));
|
||||
const nowTime = ref("");
|
||||
const currYearMonth = ref("");
|
||||
const data = reactive({});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// 左侧展示几行
|
||||
$leftLineNum: 2;
|
||||
$headerHeight: calculate.vh(80px);
|
||||
$statisticsHeight: calculate.vh(0px);
|
||||
$rightTopHeight: calculate.vh(350px);
|
||||
$leftContainerHeight: calc(
|
||||
(100vh - $headerHeight - $statisticsHeight - calculate.vh(50px))
|
||||
);
|
||||
$rightContainerBottomHeight: calc($leftContainerHeight - $rightTopHeight);
|
||||
|
||||
$top_card_height: 300px;
|
||||
$bottom_card_height: 330px;
|
||||
$card_title_height: calculate.vh(45px);
|
||||
$card_title_color: #FFF;
|
||||
$cardZIndex: 9;
|
||||
$bg: #00214e;
|
||||
$bg2: #00214eEE;
|
||||
$bg3: #00214eCC;
|
||||
|
||||
.wrap {
|
||||
background-image: url("@/assets/images/BG.png");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
&::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
z-index: 99999999;
|
||||
}
|
||||
&::after {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
box-shadow: inset 0 0 calculate.px2font(500px) #162d4b;
|
||||
z-index: 0;
|
||||
}
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: $headerHeight;
|
||||
line-height: $headerHeight;
|
||||
font-size: calculate.px2font(28px);
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
// background-image: url("@/assets/images/newtopbg.png");
|
||||
background-image: url("@/assets/images/hbg.png");
|
||||
background-size: cover;
|
||||
background-position: center 0;
|
||||
color: #c8fefd;
|
||||
letter-spacing: 0.1em;
|
||||
z-index: 999;
|
||||
letter-spacing: 0.3em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.bgtitle {
|
||||
// margin-top: 1%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calculate.vw(15px);
|
||||
font-size: calculate.px2font(32px);
|
||||
// font-style: italic;
|
||||
// font-family: YeZiGongChangAoYeHei;
|
||||
img {
|
||||
// width: 100%;
|
||||
// height: 4vh;
|
||||
width: calculate.vw(55px);
|
||||
// height: calculate.vw(55px);
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 100vw;
|
||||
height: calc($headerHeight);
|
||||
position: absolute;
|
||||
inset: 0 0 auto 0;
|
||||
z-index: -1;
|
||||
background: linear-gradient(to bottom, #0f214ced, #07204b00);
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
width: 100vw;
|
||||
height: calculate.vh(50px);
|
||||
position: fixed;
|
||||
inset: auto 0 0 0;
|
||||
z-index: 999999;
|
||||
background-image: url("../../assets/images/footerBg.png");
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: auto 0 0 0;
|
||||
z-index: 1;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
background: linear-gradient(to top,#0f214ced, #07204b00);
|
||||
}
|
||||
}
|
||||
.largeScreen__btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: calculate.vh(20px);
|
||||
right: calculate.vw(60px);
|
||||
z-index: 9999;
|
||||
cursor: pointer;
|
||||
|
||||
.rights {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calculate.px2font(10px);
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: calculate.px2font(14px);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.icon,
|
||||
img {
|
||||
width: calculate.vh(50px);
|
||||
height: calculate.vh(50px);
|
||||
object-fit: contain;
|
||||
// transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: calculate.px2font(10px);
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: calculate.px2font(10px);
|
||||
.back {
|
||||
width: calculate.vh(45px);
|
||||
height: calculate.vh(45px);
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.right-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.currDate {
|
||||
position: fixed;
|
||||
top: calculate.vw(45px);
|
||||
left: calculate.vw(10px);
|
||||
z-index: 9;
|
||||
color: #1da2fa;
|
||||
}
|
||||
}
|
||||
.totality {
|
||||
width: calculate.vw(700px);
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: calculate.vh(120px);
|
||||
z-index: 9;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
transform: translateX(-50%);
|
||||
text-align: center;
|
||||
&:deep(.el-statistic) {
|
||||
.el-statistic__head {
|
||||
font-size: calculate.px2font(18px);
|
||||
font-weight: bold;
|
||||
color: #388bdd;
|
||||
margin-bottom: calculate.vh(10px);
|
||||
}
|
||||
.el-statistic__content {
|
||||
font-size: calculate.px2font(20px);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.container {
|
||||
padding: calculate.px2font(10px) 0 0;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
margin-top: 1%;
|
||||
&__top {
|
||||
display: grid;
|
||||
grid-template-columns: 4fr 6fr 4fr;
|
||||
gap: calculate.px2font(20px);
|
||||
margin-bottom: calculate.px2font(10px);
|
||||
position: relative;
|
||||
|
||||
$lrWidth: 23vw;
|
||||
.left {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
width: $lrWidth;
|
||||
top: calculate.vh(20px);
|
||||
height: 90vh;
|
||||
&::before {
|
||||
content: "";
|
||||
width: 25vw;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
inset: calculate.vh(-90px) auto 0 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(90deg, #0f214ced,#05204fba 49%, #07204b00);
|
||||
}
|
||||
|
||||
.card {
|
||||
// background: linear-gradient(to right,$bg, $bg2, $bg3);
|
||||
// border-radius: 0 calculate.px2font(10px) calculate.px2font(10px) 0;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calculate.vh(20px);
|
||||
z-index: 9;
|
||||
width: $lrWidth;
|
||||
height: 90vh;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
width: 25vw;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
top: calculate.vh(-90px);
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background: linear-gradient(270deg,#0f214ced,#05204fba 49%, #07204b00);
|
||||
}
|
||||
.card {
|
||||
// background: linear-gradient(to left,$bg, $bg2, $bg3);
|
||||
// border-radius: calculate.px2font(10px) 0 0 calculate.px2font(10px);
|
||||
}
|
||||
}
|
||||
.timeline {
|
||||
position: fixed;
|
||||
left: 28%;
|
||||
bottom: 18%;
|
||||
width: 44vw;
|
||||
height: 80px;
|
||||
z-index: 99;
|
||||
}
|
||||
.flexcolumns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 2;
|
||||
}
|
||||
.middle {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1;
|
||||
font-size: calculate.px2font(16px);
|
||||
// position: relative;
|
||||
// &::after {
|
||||
// content: "";
|
||||
// width: 100vw;
|
||||
// height: 100vh;
|
||||
// position: absolute;
|
||||
// inset: 0;
|
||||
// padding: calculate.px2font(200px);
|
||||
// box-shadow: inset 0 0 calculate.px2font(500px) #162d4b;
|
||||
// z-index: 0;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flex-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.card {
|
||||
height: 43vh;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: calculate.vh(15px) calculate.vw(15px);
|
||||
&__title {
|
||||
// display: none;
|
||||
padding: 0 calculate.px2font(10px) 0 calculate.px2font(40px);
|
||||
font-weight: bold;
|
||||
font-size: calculate.px2font(15px);
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
// height: $card_title_height;
|
||||
line-height: $card_title_height;
|
||||
margin-bottom: calculate.vh(15px);
|
||||
background: url("@/assets/images/largeScreenImage/topbg.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
position: relative;
|
||||
color: $card_title_color;
|
||||
letter-spacing: 0.1em;
|
||||
// &::before, &::after {
|
||||
// content: "";
|
||||
// width: calculate.vw(15px);
|
||||
// height: calculate.vw(10px);
|
||||
// position: absolute;
|
||||
// top: 65%;
|
||||
// z-index: 9;
|
||||
// transform: translateY(-50%);
|
||||
// clip-path: polygon(50% 0%, 100% 0%, 50% 100%, 0% 100%);
|
||||
// // background: url("@/assets/images/largeScreenImage/cardTitleIcon@2x.png")
|
||||
// // no-repeat;
|
||||
// // background-size: 100% 100%;
|
||||
// }
|
||||
// &::before {
|
||||
// left: calculate.vw(5px);
|
||||
// background-color: #1A87D2;
|
||||
// }
|
||||
// &::after {
|
||||
// left: calculate.vw(18px);
|
||||
// background-color: #FFF;
|
||||
// }
|
||||
&:deep(.el-tabs) {
|
||||
.el-tabs__header {
|
||||
margin: 0;
|
||||
}
|
||||
.el-tabs__item {
|
||||
font-size: calculate.px2font(16px);
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
width: 100%;
|
||||
height: calc(100% - $card_title_height);
|
||||
// border-radius: 10px;
|
||||
background-image: url("@/assets/images/cardBg.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
z-index: 2;
|
||||
overflow: hidden;
|
||||
&-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.titles {
|
||||
display: none;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 1vw;
|
||||
width: 100%;
|
||||
line-height: calculate.vh(45px);
|
||||
// transform: scale(1, 0.85) skewX(-7deg);
|
||||
padding: calculate.vh(10px) calculate.vh(15px);
|
||||
box-sizing: border-box;
|
||||
background-image: url("../../assets/images/titbg2.png");
|
||||
// background-image: url("@/assets/images/largeScreenImage/cardHeader.png");
|
||||
background-size: 100% 100%; /* 或 contain,根据需求调整 */
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
// position: relative;
|
||||
// &::after {
|
||||
// content: "";
|
||||
// position: absolute;
|
||||
// inset: auto 0 calculate.vh(1px) 0;
|
||||
// z-index: -1;
|
||||
// width: 100%;
|
||||
// height: 30px;
|
||||
|
||||
// }
|
||||
}
|
||||
}
|
||||
.noBg {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
.card-1 {
|
||||
height: $rightTopHeight;
|
||||
}
|
||||
.card-2 {
|
||||
height: $rightContainerBottomHeight;
|
||||
}
|
||||
.card2 {
|
||||
&__title {
|
||||
background-image: url("@/assets/images/largeScreenImage/topbg.png");
|
||||
}
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: calculate.px2font(10px);
|
||||
}
|
||||
.gap20 {
|
||||
display: flex;
|
||||
gap: calculate.vw(20px);
|
||||
}
|
||||
:deep(.leaflet-bar) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.elegant-collapse-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(24, 75, 112, 0.6),
|
||||
rgba(24, 75, 112, 0.3)
|
||||
);
|
||||
box-shadow: inset 0 0 14px 20px rgba(36, 55, 87, 0.7),
|
||||
inset 0 0 20px 100px rgba(36, 55, 87, 0.3);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(91, 143, 249, 0.15);
|
||||
}
|
||||
|
||||
.elegant-collapse {
|
||||
border: none;
|
||||
|
||||
:deep(.el-collapse-item__header) {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
padding: 0 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
background: transparent;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
.el-collapse-item__arrow {
|
||||
font-size: 25px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__wrap) {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__content) {
|
||||
padding: 20px 0;
|
||||
color: #4a5b6c;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-content {
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
.imgList {
|
||||
width: 100%;
|
||||
height: 20vh;
|
||||
padding-bottom: 4vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(91, 143, 249, 0.5);
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(91, 143, 249, 0.8);
|
||||
}
|
||||
}
|
||||
img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.data-item {
|
||||
background: rgba(235, 245, 255, 0.5);
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: #6b7c93;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
|
||||
&.positive {
|
||||
color: #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
color: #6b7c93;
|
||||
}
|
||||
|
||||
.metric-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(235, 245, 255, 0.5);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
font-size: 24px;
|
||||
margin-right: 12px;
|
||||
color: #5b8ff9;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.metric-title {
|
||||
font-size: 13px;
|
||||
color: #6b7c93;
|
||||
}
|
||||
|
||||
:deep(.custam-select){
|
||||
$inputHeight: calculate.vh(45px);
|
||||
$selectBg: #16437F;
|
||||
.el-select__wrapper {
|
||||
box-shadow: 0 0 0 calculate.px2font(1px) #2d6691 inset;
|
||||
padding: calculate.vh(10px) calculate.vw(15px);
|
||||
font-size: calculate.px2font(18px);
|
||||
width: 100%;
|
||||
height: $inputHeight;
|
||||
overflow: hidden;
|
||||
min-height: $inputHeight;
|
||||
line-height: $inputHeight;
|
||||
/* padding: 6%; */
|
||||
background: transparent;
|
||||
color: #1890ff;
|
||||
background-color: $selectBg;
|
||||
border-radius: calculate.px2font(5px);
|
||||
|
||||
.el-select__input {
|
||||
height: 100%;
|
||||
}
|
||||
.el-select__caret {
|
||||
font-size: inherit;
|
||||
}
|
||||
.el-icon svg {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
.el-select__placeholder {
|
||||
color: #FFF;
|
||||
font-size: calculate.px2font(14px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
@use "@/assets/styles/computed.scss" as calculate;
|
||||
.custam-select-pupper {
|
||||
min-width: calculate.vh(10px);
|
||||
line-height: calculate.vh(20px);
|
||||
border-radius: calculate.px2font(5px);
|
||||
font-size: calculate.px2font(20px);
|
||||
&.is-light {
|
||||
border-width: calculate.px2font(2px);
|
||||
}
|
||||
&[data-popper-placement^=left]>.el-popper__arrow {
|
||||
right: calculate.vh(-5px);
|
||||
}
|
||||
&[data-popper-placement^=bottom]>.el-popper__arrow {
|
||||
top: calculate.vh(-5px);
|
||||
}
|
||||
.el-popper__arrow:before {
|
||||
width: calculate.vh(10px);
|
||||
height: calculate.vh(10px);
|
||||
}
|
||||
.el-select-dropdown__wrap {
|
||||
max-height: calculate.vh(280px);
|
||||
}
|
||||
.el-select-dropdown__list {
|
||||
padding: calculate.vh(10px) 0;
|
||||
}
|
||||
.el-select-dropdown__item {
|
||||
height: calculate.vh(45px);
|
||||
line-height: calculate.vh(45px);
|
||||
font-size: calculate.px2font(16px);
|
||||
padding: 0 calculate.vw(45px) 0 calculate.vw(20px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<div id="demo">
|
||||
<iframe src="https://10.22.245.209:18888/ktCallingSystem/wholeCallPage?agentId= &agentPwd= " id="iframe"
|
||||
style="width:108px;height:108px;right:20px;bottom:20px;" allow="microphone;camera;midi;encrypted-media">
|
||||
</iframe>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped lang="less"></style>
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true">
|
||||
<el-form-item label="培训机构" prop="noticeTitle">
|
||||
<el-input
|
||||
v-model="queryParams.noticeTitle"
|
||||
placeholder="请输入培训机构"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="createBy">
|
||||
<el-input
|
||||
v-model="queryParams.createBy"
|
||||
placeholder="请输入联系电话"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="考试计划通知电话1" prop="createBy">
|
||||
<el-input
|
||||
v-model="queryParams.createBy"
|
||||
placeholder="考试计划通知电话1"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="考试计划通知电话2" prop="createBy">
|
||||
<el-input
|
||||
v-model="queryParams.createBy"
|
||||
placeholder="考试计划通知电话2"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery"
|
||||
>重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['system:notice:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>编辑</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['system:notice:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
@queryTable="getList"
|
||||
></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="noticeList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column
|
||||
label="培训机构名称"
|
||||
align="center"
|
||||
prop="noticeTitle"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="联系电话"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="考试计划通知电话1"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="考试计划通知电话2"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="地址"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:notice:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改公告对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="780px" append-to-body>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="160px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="培训机构名称" prop="noticeTitle">
|
||||
<el-input
|
||||
v-model="form.noticeTitle"
|
||||
placeholder="培训机构名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="noticeType">
|
||||
<el-select
|
||||
v-model="form.noticeType"
|
||||
placeholder="联系电话"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_notice_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="考试计划通知电话1" prop="noticeTitle">
|
||||
<el-input v-model="form.noticeTitle" placeholder="考试计划通知电话1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="考试计划通知电话2" prop="noticeType">
|
||||
<el-input v-model="form.noticeTitle" placeholder="考试计划通知电话2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="地址" prop="noticeTitle">
|
||||
<el-input type="textarea" v-model="form.noticeTitle" placeholder="地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listNotice,
|
||||
getNotice,
|
||||
delNotice,
|
||||
addNotice,
|
||||
updateNotice,
|
||||
} from "@/api/system/notice";
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { useDict } from "@/utils/dict";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "Notice",
|
||||
setup() {
|
||||
const { dict } = useDict(["sys_notice_status", "sys_notice_type"]);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const noticeList = ref([]);
|
||||
const title = ref("");
|
||||
const open = ref(false);
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
noticeTitle: undefined,
|
||||
createBy: undefined,
|
||||
status: undefined,
|
||||
});
|
||||
|
||||
const form = reactive({
|
||||
noticeId: undefined,
|
||||
noticeTitle: undefined,
|
||||
noticeType: undefined,
|
||||
noticeContent: undefined,
|
||||
status: "0",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
noticeTitle: [
|
||||
{ required: true, message: "培训机构不能为空", trigger: "blur" },
|
||||
],
|
||||
noticeType: [
|
||||
{ required: true, message: "公告类型不能为空", trigger: "change" },
|
||||
],
|
||||
});
|
||||
|
||||
// 获取表单和查询表单的引用
|
||||
const formRef = ref(null);
|
||||
const queryFormRef = ref(null);
|
||||
|
||||
// 方法定义
|
||||
const getList = () => {
|
||||
loading.value = true;
|
||||
listNotice(queryParams).then((response) => {
|
||||
noticeList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
open.value = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
Object.assign(form, {
|
||||
noticeId: undefined,
|
||||
noticeTitle: undefined,
|
||||
noticeType: undefined,
|
||||
noticeContent: undefined,
|
||||
status: "0",
|
||||
});
|
||||
// formRef.value.resetFields();
|
||||
};
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
const handleSelectionChange = (selection) => {
|
||||
ids.value = selection.map((item) => item.noticeId);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
open.value = true;
|
||||
title.value = "添加培训机构";
|
||||
};
|
||||
|
||||
const handleUpdate = (row) => {
|
||||
reset();
|
||||
const noticeId = row.noticeId || ids.value;
|
||||
getNotice(noticeId).then((response) => {
|
||||
Object.assign(form, response.data);
|
||||
open.value = true;
|
||||
title.value = "修改公告";
|
||||
});
|
||||
};
|
||||
|
||||
const submitForm = () => {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.noticeId != undefined) {
|
||||
updateNotice(form).then((response) => {
|
||||
ElMessage.success("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addNotice(form).then((response) => {
|
||||
ElMessage.success("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (row) => {
|
||||
const noticeIds = row.noticeId || ids.value;
|
||||
ElMessageBox.confirm(
|
||||
'是否确认删除公告编号为"' + noticeIds + '"的数据项?',
|
||||
"提示",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
return delNotice(noticeIds);
|
||||
})
|
||||
.then(() => {
|
||||
getList();
|
||||
ElMessage.success("删除成功");
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
return {
|
||||
dict,
|
||||
loading,
|
||||
ids,
|
||||
single,
|
||||
multiple,
|
||||
showSearch,
|
||||
total,
|
||||
noticeList,
|
||||
title,
|
||||
open,
|
||||
queryParams,
|
||||
form,
|
||||
rules,
|
||||
formRef,
|
||||
queryFormRef,
|
||||
getList,
|
||||
cancel,
|
||||
reset,
|
||||
handleQuery,
|
||||
resetQuery,
|
||||
handleSelectionChange,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
submitForm,
|
||||
handleDelete,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,537 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true">
|
||||
<el-form-item label="培训班名称" prop="noticeTitle">
|
||||
<el-input
|
||||
v-model="queryParams.noticeTitle"
|
||||
placeholder="培训班名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" size="mini" @click="handleQuery"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button icon="Refresh" size="mini" @click="resetQuery"
|
||||
>重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="noticeList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column
|
||||
label="培训班名称"
|
||||
align="center"
|
||||
prop="noticeTitle"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="理论计划培训时间"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="实操计划培训时间"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="班级状态"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="报名情况"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>详情</el-button
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="studentBM(scope.row)"
|
||||
v-hasPermi="['system:notice:remove']"
|
||||
>学员报名</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改公告对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="780px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报名或考试" prop="noticeTitle">
|
||||
<el-input
|
||||
v-model="form.noticeTitle"
|
||||
placeholder="请输入报名或考试"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班级或个人" prop="noticeType">
|
||||
<el-select
|
||||
v-model="form.noticeType"
|
||||
placeholder="请选择班级或个人"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_notice_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="考试类型" prop="noticeTitle">
|
||||
<el-input v-model="form.noticeTitle" placeholder="考试类型" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="岗位类别" prop="noticeType">
|
||||
<el-select v-model="form.noticeType" placeholder="岗位类别">
|
||||
<el-option
|
||||
v-for="dict in sys_notice_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资料类型" prop="noticeTitle">
|
||||
<el-input v-model="form.noticeTitle" placeholder="资料类型" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否必须" prop="noticeType">
|
||||
<el-select v-model="form.noticeType" placeholder="是否必须">
|
||||
<el-option
|
||||
v-for="dict in sys_notice_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="序号" prop="noticeTitle">
|
||||
<el-input v-model="form.noticeTitle" placeholder="序号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资料要求描述" prop="noticeType">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="form.noticeTitle"
|
||||
placeholder="资料要求描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 学生报名 -->
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="studentBMopen"
|
||||
width="980px"
|
||||
append-to-body
|
||||
>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
size="mini"
|
||||
v-hasPermi="['system:notice:add']"
|
||||
>添加学员</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>批量导入</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>批量导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>资料导入</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>资料导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
v-hasPermi="['system:notice:edit']"
|
||||
>批量审核</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
v-hasPermi="['system:notice:remove']"
|
||||
>批量删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="noticeList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column
|
||||
label="课时"
|
||||
align="center"
|
||||
prop="noticeTitle"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="身份证复印件"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="照片"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="健康承诺"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="证件集"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width=""
|
||||
/>
|
||||
<el-table-column
|
||||
label="考核申请表"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="无违规证明"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="任职文件"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
label="学历证明"
|
||||
align="center"
|
||||
prop="createBy"
|
||||
width="160"
|
||||
/>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="studentBMopen = false">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listNotice,
|
||||
getNotice,
|
||||
delNotice,
|
||||
addNotice,
|
||||
updateNotice,
|
||||
} from "@/api/system/notice";
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { useDict } from "@/utils/dict";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "Notice",
|
||||
setup() {
|
||||
const { dict } = useDict(["sys_notice_status", "sys_notice_type"]);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const noticeList = ref([]);
|
||||
const title = ref("");
|
||||
const open = ref(false);
|
||||
const studentBMopen = ref(false);
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
noticeTitle: undefined,
|
||||
createBy: undefined,
|
||||
status: undefined,
|
||||
});
|
||||
|
||||
const form = reactive({
|
||||
noticeId: undefined,
|
||||
noticeTitle: undefined,
|
||||
noticeType: undefined,
|
||||
noticeContent: undefined,
|
||||
status: "0",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
noticeTitle: [
|
||||
{ required: true, message: "公告标题不能为空", trigger: "blur" },
|
||||
],
|
||||
noticeType: [
|
||||
{ required: true, message: "公告类型不能为空", trigger: "change" },
|
||||
],
|
||||
});
|
||||
|
||||
// 获取表单和查询表单的引用
|
||||
const formRef = ref(null);
|
||||
const queryFormRef = ref(null);
|
||||
|
||||
// 方法定义
|
||||
const getList = () => {
|
||||
loading.value = true;
|
||||
listNotice(queryParams).then((response) => {
|
||||
noticeList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
open.value = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
Object.assign(form, {
|
||||
noticeId: undefined,
|
||||
noticeTitle: undefined,
|
||||
noticeType: undefined,
|
||||
noticeContent: undefined,
|
||||
status: "0",
|
||||
});
|
||||
// formRef.value.resetFields();
|
||||
};
|
||||
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
const handleSelectionChange = (selection) => {
|
||||
ids.value = selection.map((item) => item.noticeId);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
open.value = true;
|
||||
title.value = "添加资料模板";
|
||||
};
|
||||
|
||||
const handleUpdate = (row) => {
|
||||
reset();
|
||||
const noticeId = row.noticeId || ids.value;
|
||||
getNotice(noticeId).then((response) => {
|
||||
Object.assign(form, response.data);
|
||||
open.value = true;
|
||||
title.value = "修改公告";
|
||||
});
|
||||
};
|
||||
|
||||
const submitForm = () => {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.noticeId != undefined) {
|
||||
updateNotice(form).then((response) => {
|
||||
ElMessage.success("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addNotice(form).then((response) => {
|
||||
ElMessage.success("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const studentBM = (row) => {
|
||||
console.log(123);
|
||||
|
||||
studentBMopen.value = true;
|
||||
};
|
||||
const handleDelete = (row) => {
|
||||
const noticeIds = row.noticeId || ids.value;
|
||||
ElMessageBox.confirm(
|
||||
'是否确认删除公告编号为"' + noticeIds + '"的数据项?',
|
||||
"提示",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
return delNotice(noticeIds);
|
||||
})
|
||||
.then(() => {
|
||||
getList();
|
||||
ElMessage.success("删除成功");
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
return {
|
||||
dict,
|
||||
loading,
|
||||
ids,
|
||||
single,
|
||||
multiple,
|
||||
showSearch,
|
||||
total,
|
||||
noticeList,
|
||||
title,
|
||||
open,
|
||||
queryParams,
|
||||
form,
|
||||
rules,
|
||||
formRef,
|
||||
queryFormRef,
|
||||
studentBMopen,
|
||||
getList,
|
||||
cancel,
|
||||
reset,
|
||||
handleQuery,
|
||||
resetQuery,
|
||||
handleSelectionChange,
|
||||
handleAdd,
|
||||
handleUpdate,
|
||||
submitForm,
|
||||
handleDelete,
|
||||
studentBM,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input
|
||||
v-model="queryParams.phonenumber"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="openSelectUser"
|
||||
v-hasPermi="['system:role:add']"
|
||||
>添加用户</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="CircleClose"
|
||||
:disabled="multiple"
|
||||
@click="cancelAuthUserAll"
|
||||
v-hasPermi="['system:role:remove']"
|
||||
>批量取消授权</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Close"
|
||||
@click="handleClose"
|
||||
>关闭</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
<div class="tablebox">
|
||||
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']">取消授权</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AuthUser">
|
||||
import selectUser from "./selectUser";
|
||||
import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
|
||||
|
||||
const route = useRoute();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
|
||||
|
||||
const userList = ref([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const userIds = ref([]);
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleId: route.params.roleId,
|
||||
userName: undefined,
|
||||
phonenumber: undefined,
|
||||
});
|
||||
|
||||
/** 查询授权用户列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
allocatedUserList(queryParams).then(response => {
|
||||
userList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 返回按钮 */
|
||||
function handleClose() {
|
||||
const obj = { path: "/system/role" };
|
||||
proxy.$tab.closeOpenPage(obj);
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
userIds.value = selection.map(item => item.userId);
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 打开授权用户表弹窗 */
|
||||
function openSelectUser() {
|
||||
proxy.$refs["selectRef"].show();
|
||||
}
|
||||
|
||||
/** 取消授权按钮操作 */
|
||||
function cancelAuthUser(row) {
|
||||
proxy.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?').then(function () {
|
||||
return authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("取消授权成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
/** 批量取消授权按钮操作 */
|
||||
function cancelAuthUserAll(row) {
|
||||
const roleId = queryParams.roleId;
|
||||
const uIds = userIds.value.join(",");
|
||||
proxy.$modal.confirm("是否取消选中用户授权数据项?").then(function () {
|
||||
return authUserCancelAll({ roleId: roleId, userIds: uIds });
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("取消授权成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
getList();
|
||||
</script>
|
||||
|
|
@ -0,0 +1,599 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px">
|
||||
<el-row>
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input
|
||||
v-model="queryParams.roleName"
|
||||
placeholder="请输入角色名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限字符" prop="roleKey">
|
||||
<el-input
|
||||
v-model="queryParams.roleKey"
|
||||
placeholder="请输入权限字符"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="角色状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="创建时间" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
></el-date-picker>
|
||||
</el-form-item> -->
|
||||
</el-row>
|
||||
|
||||
<el-row class="dfjb">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
class="circle-plus-btn"
|
||||
v-hasPermi="['large:data:addweather']"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['large:data:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['large:data:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['large:data:export']"
|
||||
>导出</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button icon="RefreshLeft" class="Refresh" @click="resetQuery"
|
||||
>重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div class="tablebox">
|
||||
<!-- 表格数据 -->
|
||||
<el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="角色编号" prop="roleId" width="120" />
|
||||
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
|
||||
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="150" />
|
||||
<el-table-column label="显示顺序" prop="roleSort" width="100" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top" v-if="scope.row.roleId !== 1">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.roleId !== 1">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="数据权限" placement="top" v-if="scope.row.roleId !== 1">
|
||||
<el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="分配用户" placement="top" v-if="scope.row.roleId !== 1">
|
||||
<el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 添加或修改角色配置对话框 -->
|
||||
<el-dialog :title="title" :style="{ height: '700px' }" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="roleRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="form.roleName" placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="roleKey">
|
||||
<template #label>
|
||||
<span>
|
||||
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)" placement="top">
|
||||
<el-icon><question-filled /></el-icon>
|
||||
</el-tooltip>
|
||||
权限字符
|
||||
</span>
|
||||
</template>
|
||||
<el-input v-model="form.roleKey" placeholder="请输入权限字符" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色顺序" prop="roleSort">
|
||||
<el-input-number v-model="form.roleSort" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio
|
||||
v-for="dict in sys_normal_disable"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单权限">
|
||||
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
class="tree-border"
|
||||
:data="menuOptions"
|
||||
show-checkbox
|
||||
ref="menuRef"
|
||||
node-key="id"
|
||||
:check-strictly="!form.menuCheckStrictly"
|
||||
empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 分配角色数据权限对话框 -->
|
||||
<el-dialog :title="title" :style="{ height: '700px' }" v-model="openDataScope" width="500px" append-to-body>
|
||||
<el-form :model="form" label-width="80px">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input v-model="form.roleName" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限字符">
|
||||
<el-input v-model="form.roleKey" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限范围">
|
||||
<el-select v-model="form.dataScope" @change="dataScopeSelectChange">
|
||||
<el-option
|
||||
v-for="item in dataScopeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据权限" v-show="form.dataScope == 2">
|
||||
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
class="tree-border"
|
||||
:data="deptOptions"
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
ref="deptRef"
|
||||
node-key="id"
|
||||
:check-strictly="!form.deptCheckStrictly"
|
||||
empty-text="加载中,请稍候"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitDataScope">确 定</el-button>
|
||||
<el-button @click="cancelDataScope">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Role">
|
||||
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role";
|
||||
import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
|
||||
|
||||
const roleList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const dateRange = ref([]);
|
||||
const menuOptions = ref([]);
|
||||
const menuExpand = ref(false);
|
||||
const menuNodeAll = ref(false);
|
||||
const deptExpand = ref(true);
|
||||
const deptNodeAll = ref(false);
|
||||
const deptOptions = ref([]);
|
||||
const openDataScope = ref(false);
|
||||
const menuRef = ref(null);
|
||||
const deptRef = ref(null);
|
||||
|
||||
/** 数据范围选项*/
|
||||
const dataScopeOptions = ref([
|
||||
{ value: "1", label: "全部数据权限" },
|
||||
{ value: "2", label: "自定数据权限" },
|
||||
{ value: "3", label: "本部门数据权限" },
|
||||
{ value: "4", label: "本部门及以下数据权限" },
|
||||
{ value: "5", label: "仅本人数据权限" }
|
||||
]);
|
||||
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleName: undefined,
|
||||
roleKey: undefined,
|
||||
status: undefined
|
||||
},
|
||||
rules: {
|
||||
roleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }],
|
||||
roleKey: [{ required: true, message: "权限字符不能为空", trigger: "blur" }],
|
||||
roleSort: [{ required: true, message: "角色顺序不能为空", trigger: "blur" }]
|
||||
},
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询角色列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listRole(proxy.addDateRange(queryParams.value, dateRange.value)).then(response => {
|
||||
roleList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const roleIds = row.roleId || ids.value;
|
||||
proxy.$modal.confirm('是否确认删除角色编号为"' + roleIds + '"的数据项?').then(function () {
|
||||
return delRole(roleIds);
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download("system/role/export", {
|
||||
...queryParams.value,
|
||||
}, `role_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.roleId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 角色状态修改 */
|
||||
function handleStatusChange(row) {
|
||||
let text = row.status === "0" ? "启用" : "停用";
|
||||
proxy.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?').then(function () {
|
||||
return changeRoleStatus(row.roleId, row.status);
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess(text + "成功");
|
||||
}).catch(function () {
|
||||
row.status = row.status === "0" ? "1" : "0";
|
||||
});
|
||||
}
|
||||
|
||||
/** 更多操作 */
|
||||
function handleCommand(command, row) {
|
||||
switch (command) {
|
||||
case "handleDataScope":
|
||||
handleDataScope(row);
|
||||
break;
|
||||
case "handleAuthUser":
|
||||
handleAuthUser(row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** 分配用户 */
|
||||
function handleAuthUser(row) {
|
||||
router.push("/system/role-auth/user/" + row.roleId);
|
||||
}
|
||||
|
||||
/** 查询菜单树结构 */
|
||||
function getMenuTreeselect() {
|
||||
menuTreeselect().then(response => {
|
||||
menuOptions.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
/** 所有部门节点数据 */
|
||||
function getDeptAllCheckedKeys() {
|
||||
// 目前被选中的部门节点
|
||||
let checkedKeys = deptRef.value.getCheckedKeys();
|
||||
// 半选中的部门节点
|
||||
let halfCheckedKeys = deptRef.value.getHalfCheckedKeys();
|
||||
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
|
||||
return checkedKeys;
|
||||
}
|
||||
|
||||
/** 重置新增的表单以及其他数据 */
|
||||
function reset() {
|
||||
if (menuRef.value != undefined) {
|
||||
menuRef.value.setCheckedKeys([]);
|
||||
}
|
||||
menuExpand.value = false;
|
||||
menuNodeAll.value = false;
|
||||
deptExpand.value = true;
|
||||
deptNodeAll.value = false;
|
||||
form.value = {
|
||||
roleId: undefined,
|
||||
roleName: undefined,
|
||||
roleKey: undefined,
|
||||
roleSort: 0,
|
||||
status: "0",
|
||||
menuIds: [],
|
||||
deptIds: [],
|
||||
menuCheckStrictly: true,
|
||||
deptCheckStrictly: true,
|
||||
remark: undefined
|
||||
};
|
||||
proxy.resetForm("roleRef");
|
||||
}
|
||||
|
||||
/** 添加角色 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
getMenuTreeselect();
|
||||
open.value = true;
|
||||
title.value = "添加角色";
|
||||
}
|
||||
|
||||
/** 修改角色 */
|
||||
function handleUpdate(row) {
|
||||
reset();
|
||||
const roleId = row.roleId || ids.value;
|
||||
const roleMenu = getRoleMenuTreeselect(roleId);
|
||||
getRole(roleId).then(response => {
|
||||
form.value = response.data;
|
||||
form.value.roleSort = Number(form.value.roleSort);
|
||||
open.value = true;
|
||||
nextTick(() => {
|
||||
roleMenu.then((res) => {
|
||||
let checkedKeys = res.checkedKeys;
|
||||
checkedKeys.forEach((v) => {
|
||||
nextTick(() => {
|
||||
menuRef.value.setChecked(v, true, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
title.value = "修改角色";
|
||||
}
|
||||
|
||||
/** 根据角色ID查询菜单树结构 */
|
||||
function getRoleMenuTreeselect(roleId) {
|
||||
return roleMenuTreeselect(roleId).then(response => {
|
||||
menuOptions.value = response.menus;
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
/** 根据角色ID查询部门树结构 */
|
||||
function getDeptTree(roleId) {
|
||||
return deptTreeSelect(roleId).then(response => {
|
||||
deptOptions.value = response.depts;
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
/** 树权限(展开/折叠)*/
|
||||
function handleCheckedTreeExpand(value, type) {
|
||||
if (type == "menu") {
|
||||
let treeList = menuOptions.value;
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
|
||||
}
|
||||
} else if (type == "dept") {
|
||||
let treeList = deptOptions.value;
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 树权限(全选/全不选) */
|
||||
function handleCheckedTreeNodeAll(value, type) {
|
||||
if (type == "menu") {
|
||||
menuRef.value.setCheckedNodes(value ? menuOptions.value : []);
|
||||
} else if (type == "dept") {
|
||||
deptRef.value.setCheckedNodes(value ? deptOptions.value : []);
|
||||
}
|
||||
}
|
||||
|
||||
/** 树权限(父子联动) */
|
||||
function handleCheckedTreeConnect(value, type) {
|
||||
if (type == "menu") {
|
||||
form.value.menuCheckStrictly = value ? true : false;
|
||||
} else if (type == "dept") {
|
||||
form.value.deptCheckStrictly = value ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 所有菜单节点数据 */
|
||||
function getMenuAllCheckedKeys() {
|
||||
// 目前被选中的菜单节点
|
||||
let checkedKeys = menuRef.value.getCheckedKeys();
|
||||
// 半选中的菜单节点
|
||||
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys();
|
||||
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
|
||||
return checkedKeys;
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["roleRef"].validate(valid => {
|
||||
if (valid) {
|
||||
if (form.value.roleId != undefined) {
|
||||
form.value.menuIds = getMenuAllCheckedKeys();
|
||||
updateRole(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
form.value.menuIds = getMenuAllCheckedKeys();
|
||||
addRole(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 取消按钮 */
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
/** 选择角色权限范围触发 */
|
||||
function dataScopeSelectChange(value) {
|
||||
if (value !== "2") {
|
||||
deptRef.value.setCheckedKeys([]);
|
||||
}
|
||||
}
|
||||
|
||||
/** 分配数据权限操作 */
|
||||
function handleDataScope(row) {
|
||||
reset();
|
||||
const deptTreeSelect = getDeptTree(row.roleId);
|
||||
getRole(row.roleId).then(response => {
|
||||
form.value = response.data;
|
||||
openDataScope.value = true;
|
||||
nextTick(() => {
|
||||
deptTreeSelect.then(res => {
|
||||
nextTick(() => {
|
||||
if (deptRef.value) {
|
||||
deptRef.value.setCheckedKeys(res.checkedKeys);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
title.value = "分配数据权限";
|
||||
}
|
||||
|
||||
/** 提交按钮(数据权限) */
|
||||
function submitDataScope() {
|
||||
if (form.value.roleId != undefined) {
|
||||
form.value.deptIds = getDeptAllCheckedKeys();
|
||||
dataScope(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
openDataScope.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** 取消按钮(数据权限)*/
|
||||
function cancelDataScope() {
|
||||
openDataScope.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
getList();
|
||||
</script>
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<!-- 授权用户 -->
|
||||
<el-dialog title="选择用户" v-model="visible" width="800px" top="5vh" append-to-body>
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入用户名称"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input
|
||||
v-model="queryParams.phonenumber"
|
||||
placeholder="请输入手机号码"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row>
|
||||
<el-table @row-click="clickRow" ref="refTable" :data="userList" @selection-change="handleSelectionChange" height="260px">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="用户名称" prop="userName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="用户昵称" prop="nickName" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="邮箱" prop="email" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="手机" prop="phonenumber" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSelectUser">确 定</el-button>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="SelectUser">
|
||||
import { authUserSelectAll, unallocatedUserList } from "@/api/system/role";
|
||||
|
||||
const props = defineProps({
|
||||
roleId: {
|
||||
type: [Number, String]
|
||||
}
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { sys_normal_disable } = proxy.useDict("sys_normal_disable");
|
||||
|
||||
const userList = ref([]);
|
||||
const visible = ref(false);
|
||||
const total = ref(0);
|
||||
const userIds = ref([]);
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleId: undefined,
|
||||
userName: undefined,
|
||||
phonenumber: undefined
|
||||
});
|
||||
|
||||
// 显示弹框
|
||||
function show() {
|
||||
queryParams.roleId = props.roleId;
|
||||
getList();
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
/**选择行 */
|
||||
function clickRow(row) {
|
||||
proxy.$refs["refTable"].toggleRowSelection(row);
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
userIds.value = selection.map(item => item.userId);
|
||||
}
|
||||
|
||||
// 查询表数据
|
||||
function getList() {
|
||||
unallocatedUserList(queryParams).then(res => {
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
});
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
const emit = defineEmits(["ok"]);
|
||||
/** 选择授权用户操作 */
|
||||
function handleSelectUser() {
|
||||
const roleId = queryParams.roleId;
|
||||
const uIds = userIds.value.join(",");
|
||||
if (uIds == "") {
|
||||
proxy.$modal.msgError("请选择要分配的用户");
|
||||
return;
|
||||
}
|
||||
authUserSelectAll({ roleId: roleId, userIds: uIds }).then(res => {
|
||||
proxy.$modal.msgSuccess(res.msg);
|
||||
visible.value = false;
|
||||
emit("ok");
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,307 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<!-- <el-form-item label="预警信息id" prop="warningId">
|
||||
<el-input
|
||||
v-model="queryParams.warningId"
|
||||
placeholder="请输入预警信息id"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
<!-- <el-form-item label="类型" prop="types">
|
||||
<el-input
|
||||
v-model="queryParams.types"
|
||||
placeholder="请输入类型"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="类型" prop="types">
|
||||
<el-select
|
||||
v-model="queryParams.types"
|
||||
placeholder="类型"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in supervise_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="内容" prop="contents">
|
||||
<el-input
|
||||
v-model="queryParams.contents"
|
||||
placeholder="请输入内容"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="superviseList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<!-- <el-table-column label="id" align="center" prop="id" /> -->
|
||||
<el-table-column label="预警信息" align="center" prop="warningName" />
|
||||
<el-table-column label="类型" align="center" prop="types">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="supervise_type" :value="scope.row.types" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="内容" align="center" prop="contents" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" >修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" >删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 添加或修改督办信息对话框 -->
|
||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||
<el-form ref="superviseRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="预警信息" prop="warningId">
|
||||
<!-- <el-input v-model="form.warningId" placeholder="请输入预警信息id" /> -->
|
||||
<el-select
|
||||
v-model="form.warningId"
|
||||
placeholder="预警信息"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in yjList"
|
||||
:key="dict.id"
|
||||
:label="dict.warningSigns"
|
||||
:value="dict.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="types">
|
||||
<el-select
|
||||
v-model="form.types"
|
||||
placeholder="类型"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in supervise_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="内容" prop="contents">
|
||||
<el-input v-model="form.contents" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Supervise">
|
||||
import { listSupervise, getSupervise, delSupervise, addSupervise, updateSupervise } from "@/api/supervise";
|
||||
const { proxy } = getCurrentInstance();
|
||||
import {
|
||||
getYujinList,
|
||||
|
||||
} from "@/api/map.js";
|
||||
const { supervise_type } = proxy.useDict(
|
||||
"supervise_type"
|
||||
);
|
||||
|
||||
const superviseList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref("");
|
||||
const yjList = ref([]);
|
||||
const data = reactive({
|
||||
form: {},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
warningId: null,
|
||||
types: null,
|
||||
contents: null
|
||||
},
|
||||
rules: {
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询督办信息列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listSupervise(queryParams.value).then(response => {
|
||||
superviseList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function getYjList() {
|
||||
getYujinList().then(response => {
|
||||
yjList.value = response.rows;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
open.value = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
// 表单重置
|
||||
function reset() {
|
||||
form.value = {
|
||||
id: null,
|
||||
warningId: null,
|
||||
types: null,
|
||||
createBy: null,
|
||||
createTime: null,
|
||||
updateBy: null,
|
||||
updateTime: null,
|
||||
delFlag: null,
|
||||
contents: null
|
||||
};
|
||||
proxy.resetForm("superviseRef");
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
proxy.resetForm("queryRef");
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 新增按钮操作 */
|
||||
function handleAdd() {
|
||||
reset();
|
||||
open.value = true;
|
||||
title.value = "添加督办信息";
|
||||
}
|
||||
|
||||
/** 修改按钮操作 */
|
||||
function handleUpdate(row) {
|
||||
reset();
|
||||
const _id = row.id || ids.value
|
||||
getSupervise(_id).then(response => {
|
||||
form.value = response.data;
|
||||
open.value = true;
|
||||
title.value = "修改督办信息";
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs["superviseRef"].validate(valid => {
|
||||
if (valid) {
|
||||
if (form.value.id != null) {
|
||||
updateSupervise(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
} else {
|
||||
addSupervise(form.value).then(response => {
|
||||
proxy.$modal.msgSuccess("新增成功");
|
||||
open.value = false;
|
||||
getList();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
function handleDelete(row) {
|
||||
const _ids = row.id || ids.value;
|
||||
proxy.$modal.confirm('是否确认删除督办信息?').then(function() {
|
||||
return delSupervise(_ids);
|
||||
}).then(() => {
|
||||
getList();
|
||||
proxy.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
function handleExport() {
|
||||
proxy.download('system/supervise/export', {
|
||||
...queryParams.value
|
||||
}, `supervise_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
|
||||
getList();
|
||||
getYjList();
|
||||
</script>
|
||||
Loading…
Reference in New Issue