系统整体功能测试
This commit is contained in:
parent
15ececa6b2
commit
bd6260850e
|
|
@ -0,0 +1,62 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询预警信息列表
|
||||||
|
export function listInformation(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/information/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询预警信息详细
|
||||||
|
export function getInformation(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/information/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增预警信息
|
||||||
|
export function addInformation(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/information',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改预警信息
|
||||||
|
export function updateInformation(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/information',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除预警信息
|
||||||
|
export function delInformation(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/information/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询名称列表
|
||||||
|
export function nameList(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/rules/listAll',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下发弹窗的提交按钮
|
||||||
|
export function xiafaSrue(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/handle',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询预警信息处置列表
|
||||||
|
export function listHandle(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/handle/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询预警信息处置详细
|
||||||
|
export function getHandle(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/handle/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增预警信息处置
|
||||||
|
export function addHandle(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/handle',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 受理
|
||||||
|
export function addprocess(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/process',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 修改预警信息处置
|
||||||
|
export function updateHandle(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/handle',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除预警信息处置
|
||||||
|
export function delHandle(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/handle/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询容量阀值列表
|
||||||
|
export function listThreshold(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/threshold/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询容量阀值详细
|
||||||
|
export function getThreshold(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/threshold/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增容量阀值
|
||||||
|
export function addThreshold(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/threshold',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改容量阀值
|
||||||
|
export function updateThreshold(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/threshold',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除容量阀值
|
||||||
|
export function delThreshold(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/threshold/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import request from "@/utils/request";
|
||||||
|
import signMd5Utils from "./signMd5Utils";
|
||||||
|
|
||||||
|
// 列表
|
||||||
|
export function getklList(query) {
|
||||||
|
return request({
|
||||||
|
url: "/large/videoStorageInformation/list",
|
||||||
|
method: "get",
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增菜单
|
||||||
|
export function getklListadd(data) {
|
||||||
|
return request({
|
||||||
|
url: "/large/videoStorageInformation",
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改菜单
|
||||||
|
export function getklListedit(data) {
|
||||||
|
return request({
|
||||||
|
url: "/large/videoStorageInformation",
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除菜单
|
||||||
|
export function getklListremove(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/videoStorageInformation/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改按钮回显
|
||||||
|
export function getklListedithx(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/videoStorageInformation/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 活动list
|
||||||
|
export function getsbLists(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/activity/listAll',
|
||||||
|
method: "get",
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备list
|
||||||
|
export function getsbLists2(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/videoStorageInformation/listAll',
|
||||||
|
method: "get",
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询天气信息列表
|
||||||
|
export function listWeather(query) {
|
||||||
|
return request({
|
||||||
|
url: '/large/weather/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询天气信息详细
|
||||||
|
export function getWeather(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/weather/' + id,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增天气信息
|
||||||
|
export function addWeather(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/weather',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改天气信息
|
||||||
|
export function updateWeather(data) {
|
||||||
|
return request({
|
||||||
|
url: '/large/weather',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除天气信息
|
||||||
|
export function delWeather(id) {
|
||||||
|
return request({
|
||||||
|
url: '/large/weather/' + id,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
|
||||||
<transition-group name="breadcrumb">
|
|
||||||
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
|
|
||||||
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
|
|
||||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
|
|
||||||
</el-breadcrumb-item>
|
|
||||||
</transition-group>
|
|
||||||
</el-breadcrumb>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
|
||||||
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
const permissionStore = usePermissionStore()
|
|
||||||
const levelList = ref([])
|
|
||||||
|
|
||||||
function getBreadcrumb() {
|
|
||||||
// only show routes with meta.title
|
|
||||||
let matched = []
|
|
||||||
const pathNum = findPathNum(route.path)
|
|
||||||
// multi-level menu
|
|
||||||
if (pathNum > 2) {
|
|
||||||
const reg = /\/\w+/gi
|
|
||||||
const pathList = route.path.match(reg).map((item, index) => {
|
|
||||||
if (index !== 0) item = item.slice(1)
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
getMatched(pathList, permissionStore.defaultRoutes, matched)
|
|
||||||
} else {
|
|
||||||
matched = route.matched.filter((item) => item.meta && item.meta.title)
|
|
||||||
}
|
|
||||||
// 判断是否为首页
|
|
||||||
if (!isDashboard(matched[0])) {
|
|
||||||
matched = [{ path: "/index", meta: { title: "首页" } }].concat(matched)
|
|
||||||
}
|
|
||||||
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
|
||||||
}
|
|
||||||
function findPathNum(str, char = "/") {
|
|
||||||
let index = str.indexOf(char)
|
|
||||||
let num = 0
|
|
||||||
while (index !== -1) {
|
|
||||||
num++
|
|
||||||
index = str.indexOf(char, index + 1)
|
|
||||||
}
|
|
||||||
return num
|
|
||||||
}
|
|
||||||
function getMatched(pathList, routeList, matched) {
|
|
||||||
let data = routeList.find(item => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0])
|
|
||||||
if (data) {
|
|
||||||
matched.push(data)
|
|
||||||
if (data.children && pathList.length) {
|
|
||||||
pathList.shift()
|
|
||||||
getMatched(pathList, data.children, matched)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function isDashboard(route) {
|
|
||||||
const name = route && route.name
|
|
||||||
if (!name) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return name.trim() === 'Index'
|
|
||||||
}
|
|
||||||
function handleLink(item) {
|
|
||||||
const { redirect, path } = item
|
|
||||||
if (redirect) {
|
|
||||||
router.push(redirect)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
router.push(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
// if you go to the redirect page, do not update the breadcrumbs
|
|
||||||
if (route.path.startsWith('/redirect/')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
getBreadcrumb()
|
|
||||||
})
|
|
||||||
getBreadcrumb()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.app-breadcrumb.el-breadcrumb {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 50px;
|
|
||||||
margin-left: 8px;
|
|
||||||
|
|
||||||
.no-redirect {
|
|
||||||
color: #97a8be;
|
|
||||||
cursor: text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="1">
|
|
||||||
日,允许的通配符[, - * ? / L W]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="2">
|
|
||||||
不指定
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="3">
|
|
||||||
周期从
|
|
||||||
<el-input-number v-model='cycle01' :min="1" :max="30" /> -
|
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="31" /> 日
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="4">
|
|
||||||
从
|
|
||||||
<el-input-number v-model='average01' :min="1" :max="30" /> 号开始,每
|
|
||||||
<el-input-number v-model='average02' :min="1" :max="31 - average01" /> 日执行一次
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="5">
|
|
||||||
每月
|
|
||||||
<el-input-number v-model='workday' :min="1" :max="31" /> 号最近的那个工作日
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="6">
|
|
||||||
本月最后一天
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="7">
|
|
||||||
指定
|
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
|
||||||
<el-option v-for="item in 31" :key="item" :label="item" :value="item" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const radioValue = ref(1)
|
|
||||||
const cycle01 = ref(1)
|
|
||||||
const cycle02 = ref(2)
|
|
||||||
const average01 = ref(1)
|
|
||||||
const average02 = ref(1)
|
|
||||||
const workday = ref(1)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([1])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, 1, 30)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 31)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, 1, 30)
|
|
||||||
average02.value = props.check(average02.value, 1, 31 - average01.value)
|
|
||||||
return average01.value + '/' + average02.value
|
|
||||||
})
|
|
||||||
const workdayTotal = computed(() => {
|
|
||||||
workday.value = props.check(workday.value, 1, 31)
|
|
||||||
return workday.value + 'W'
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.day, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, workdayTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (value === "*") {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value === "?") {
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf("-") > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else if (value.indexOf("/") > -1) {
|
|
||||||
const indexArr = value.split('/')
|
|
||||||
average01.value = Number(indexArr[0])
|
|
||||||
average02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 4
|
|
||||||
} else if (value.indexOf("W") > -1) {
|
|
||||||
const indexArr = value.split("W")
|
|
||||||
workday.value = Number(indexArr[0])
|
|
||||||
radioValue.value = 5
|
|
||||||
} else if (value === "L") {
|
|
||||||
radioValue.value = 6
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 单选按钮值变化时
|
|
||||||
function onRadioChange() {
|
|
||||||
if (radioValue.value === 2 && props.cron.week === '?') {
|
|
||||||
emit('update', 'week', '*', 'day')
|
|
||||||
}
|
|
||||||
if (radioValue.value !== 2 && props.cron.week !== '?') {
|
|
||||||
emit('update', 'week', '?', 'day')
|
|
||||||
}
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'day', '*', 'day')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'day', '?', 'day')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'day', cycleTotal.value, 'day')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
emit('update', 'day', averageTotal.value, 'day')
|
|
||||||
break
|
|
||||||
case 5:
|
|
||||||
emit('update', 'day', workdayTotal.value, 'day')
|
|
||||||
break
|
|
||||||
case 6:
|
|
||||||
emit('update', 'day', 'L', 'day')
|
|
||||||
break
|
|
||||||
case 7:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'day', checkboxString.value, 'day')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.2rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 18.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="1">
|
|
||||||
小时,允许的通配符[, - * /]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="2">
|
|
||||||
周期从
|
|
||||||
<el-input-number v-model='cycle01' :min="0" :max="22" /> -
|
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="23" /> 时
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="3">
|
|
||||||
从
|
|
||||||
<el-input-number v-model='average01' :min="0" :max="22" /> 时开始,每
|
|
||||||
<el-input-number v-model='average02' :min="1" :max="23 - average01" /> 小时执行一次
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="4">
|
|
||||||
指定
|
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
|
||||||
<el-option v-for="item in 24" :key="item" :label="item - 1" :value="item - 1" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const radioValue = ref(1)
|
|
||||||
const cycle01 = ref(0)
|
|
||||||
const cycle02 = ref(1)
|
|
||||||
const average01 = ref(0)
|
|
||||||
const average02 = ref(1)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([0])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, 0, 22)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 23)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, 0, 22)
|
|
||||||
average02.value = props.check(average02.value, 1, 23 - average01.value)
|
|
||||||
return average01.value + '/' + average02.value
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.hour, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (props.cron.min === '*') {
|
|
||||||
emit('update', 'min', '0', 'hour');
|
|
||||||
}
|
|
||||||
if (props.cron.second === '*') {
|
|
||||||
emit('update', 'second', '0', 'hour');
|
|
||||||
}
|
|
||||||
if (value === '*') {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value.indexOf('-') > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf('/') > -1) {
|
|
||||||
const indexArr = value.split('/')
|
|
||||||
average01.value = Number(indexArr[0])
|
|
||||||
average02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onRadioChange() {
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'hour', '*', 'hour')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'hour', cycleTotal.value, 'hour')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'hour', averageTotal.value, 'hour')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'hour', checkboxString.value, 'hour')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.2rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 18.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,309 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-tabs type="border-card">
|
|
||||||
<el-tab-pane label="秒" v-if="shouldHide('second')">
|
|
||||||
<CrontabSecond
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronsecond"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="分钟" v-if="shouldHide('min')">
|
|
||||||
<CrontabMin
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronmin"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="小时" v-if="shouldHide('hour')">
|
|
||||||
<CrontabHour
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronhour"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="日" v-if="shouldHide('day')">
|
|
||||||
<CrontabDay
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronday"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="月" v-if="shouldHide('month')">
|
|
||||||
<CrontabMonth
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronmonth"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="周" v-if="shouldHide('week')">
|
|
||||||
<CrontabWeek
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronweek"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="年" v-if="shouldHide('year')">
|
|
||||||
<CrontabYear
|
|
||||||
@update="updateCrontabValue"
|
|
||||||
:check="checkNumber"
|
|
||||||
:cron="crontabValueObj"
|
|
||||||
ref="cronyear"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
|
|
||||||
<div class="popup-main">
|
|
||||||
<div class="popup-result">
|
|
||||||
<p class="title">时间表达式</p>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<th v-for="item of tabTitles" :key="item">{{item}}</th>
|
|
||||||
<th>Cron 表达式</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.second.length < 10">{{crontabValueObj.second}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.second" placement="top"><span>{{crontabValueObj.second}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.min.length < 10">{{crontabValueObj.min}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.min" placement="top"><span>{{crontabValueObj.min}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.hour.length < 10">{{crontabValueObj.hour}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.hour" placement="top"><span>{{crontabValueObj.hour}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.day.length < 10">{{crontabValueObj.day}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.day" placement="top"><span>{{crontabValueObj.day}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.month.length < 10">{{crontabValueObj.month}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.month" placement="top"><span>{{crontabValueObj.month}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.week.length < 10">{{crontabValueObj.week}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.week" placement="top"><span>{{crontabValueObj.week}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span v-if="crontabValueObj.year.length < 10">{{crontabValueObj.year}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueObj.year" placement="top"><span>{{crontabValueObj.year}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
<td class="result">
|
|
||||||
<span v-if="crontabValueString.length < 90">{{crontabValueString}}</span>
|
|
||||||
<el-tooltip v-else :content="crontabValueString" placement="top"><span>{{crontabValueString}}</span></el-tooltip>
|
|
||||||
</td>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<CrontabResult :ex="crontabValueString"></CrontabResult>
|
|
||||||
|
|
||||||
<div class="pop_btn">
|
|
||||||
<el-button type="primary" @click="submitFill">确定</el-button>
|
|
||||||
<el-button type="warning" @click="clearCron">重置</el-button>
|
|
||||||
<el-button @click="hidePopup">取消</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import CrontabSecond from "./second.vue"
|
|
||||||
import CrontabMin from "./min.vue"
|
|
||||||
import CrontabHour from "./hour.vue"
|
|
||||||
import CrontabDay from "./day.vue"
|
|
||||||
import CrontabMonth from "./month.vue"
|
|
||||||
import CrontabWeek from "./week.vue"
|
|
||||||
import CrontabYear from "./year.vue"
|
|
||||||
import CrontabResult from "./result.vue"
|
|
||||||
const { proxy } = getCurrentInstance()
|
|
||||||
const emit = defineEmits(['hide', 'fill'])
|
|
||||||
const props = defineProps({
|
|
||||||
hideComponent: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
expression: {
|
|
||||||
type: String,
|
|
||||||
default: ""
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const tabTitles = ref(["秒", "分钟", "小时", "日", "月", "周", "年"])
|
|
||||||
const tabActive = ref(0)
|
|
||||||
const hideComponent = ref([])
|
|
||||||
const expression = ref('')
|
|
||||||
const crontabValueObj = ref({
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
})
|
|
||||||
const crontabValueString = computed(() => {
|
|
||||||
const obj = crontabValueObj.value
|
|
||||||
return obj.second
|
|
||||||
+ " "
|
|
||||||
+ obj.min
|
|
||||||
+ " "
|
|
||||||
+ obj.hour
|
|
||||||
+ " "
|
|
||||||
+ obj.day
|
|
||||||
+ " "
|
|
||||||
+ obj.month
|
|
||||||
+ " "
|
|
||||||
+ obj.week
|
|
||||||
+ (obj.year === "" ? "" : " " + obj.year)
|
|
||||||
})
|
|
||||||
watch(expression, () => resolveExp())
|
|
||||||
function shouldHide(key) {
|
|
||||||
return !(hideComponent.value && hideComponent.value.includes(key))
|
|
||||||
}
|
|
||||||
function resolveExp() {
|
|
||||||
// 反解析 表达式
|
|
||||||
if (expression.value) {
|
|
||||||
const arr = expression.value.split(/\s+/)
|
|
||||||
if (arr.length >= 6) {
|
|
||||||
//6 位以上是合法表达式
|
|
||||||
let obj = {
|
|
||||||
second: arr[0],
|
|
||||||
min: arr[1],
|
|
||||||
hour: arr[2],
|
|
||||||
day: arr[3],
|
|
||||||
month: arr[4],
|
|
||||||
week: arr[5],
|
|
||||||
year: arr[6] ? arr[6] : ""
|
|
||||||
}
|
|
||||||
crontabValueObj.value = {
|
|
||||||
...obj,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 没有传入的表达式 则还原
|
|
||||||
clearCron()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// tab切换值
|
|
||||||
function tabCheck(index) {
|
|
||||||
tabActive.value = index
|
|
||||||
}
|
|
||||||
// 由子组件触发,更改表达式组成的字段值
|
|
||||||
function updateCrontabValue(name, value, from) {
|
|
||||||
crontabValueObj.value[name] = value
|
|
||||||
}
|
|
||||||
// 表单选项的子组件校验数字格式(通过-props传递)
|
|
||||||
function checkNumber(value, minLimit, maxLimit) {
|
|
||||||
// 检查必须为整数
|
|
||||||
value = Math.floor(value)
|
|
||||||
if (value < minLimit) {
|
|
||||||
value = minLimit
|
|
||||||
} else if (value > maxLimit) {
|
|
||||||
value = maxLimit
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
// 隐藏弹窗
|
|
||||||
function hidePopup() {
|
|
||||||
emit("hide")
|
|
||||||
}
|
|
||||||
// 填充表达式
|
|
||||||
function submitFill() {
|
|
||||||
emit("fill", crontabValueString.value)
|
|
||||||
hidePopup()
|
|
||||||
}
|
|
||||||
function clearCron() {
|
|
||||||
// 还原选择项
|
|
||||||
crontabValueObj.value = {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
expression.value = props.expression
|
|
||||||
hideComponent.value = props.hideComponent
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.pop_btn {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
.popup-main {
|
|
||||||
position: relative;
|
|
||||||
margin: 10px auto;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.popup-title {
|
|
||||||
overflow: hidden;
|
|
||||||
line-height: 34px;
|
|
||||||
padding-top: 6px;
|
|
||||||
background: #f2f2f2;
|
|
||||||
}
|
|
||||||
.popup-result {
|
|
||||||
box-sizing: border-box;
|
|
||||||
line-height: 24px;
|
|
||||||
margin: 25px auto;
|
|
||||||
padding: 15px 10px 10px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.popup-result .title {
|
|
||||||
position: absolute;
|
|
||||||
top: -28px;
|
|
||||||
left: 50%;
|
|
||||||
width: 140px;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-left: -70px;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 30px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.popup-result table {
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.popup-result table td:not(.result) {
|
|
||||||
width: 3.5rem;
|
|
||||||
min-width: 3.5rem;
|
|
||||||
max-width: 3.5rem;
|
|
||||||
}
|
|
||||||
.popup-result table span {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
font-family: arial;
|
|
||||||
line-height: 30px;
|
|
||||||
height: 30px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid #e8e8e8;
|
|
||||||
}
|
|
||||||
.popup-result-scroll {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 24px;
|
|
||||||
height: 10em;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="1">
|
|
||||||
分钟,允许的通配符[, - * /]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="2">
|
|
||||||
周期从
|
|
||||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 分钟
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="3">
|
|
||||||
从
|
|
||||||
<el-input-number v-model='average01' :min="0" :max="58" /> 分钟开始, 每
|
|
||||||
<el-input-number v-model='average02' :min="1" :max="59 - average01" /> 分钟执行一次
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="4">
|
|
||||||
指定
|
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
|
||||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const radioValue = ref(1)
|
|
||||||
const cycle01 = ref(0)
|
|
||||||
const cycle02 = ref(1)
|
|
||||||
const average01 = ref(0)
|
|
||||||
const average02 = ref(1)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([0])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, 0, 58)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, 0, 58)
|
|
||||||
average02.value = props.check(average02.value, 1, 59 - average01.value)
|
|
||||||
return average01.value + '/' + average02.value
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.min, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (value === '*') {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value.indexOf('-') > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf('/') > -1) {
|
|
||||||
const indexArr = value.split('/')
|
|
||||||
average01.value = Number(indexArr[0])
|
|
||||||
average02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onRadioChange() {
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'min', '*', 'min')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'min', cycleTotal.value, 'min')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'min', averageTotal.value, 'min')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'min', checkboxString.value, 'min')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.2rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 19.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="1">
|
|
||||||
月,允许的通配符[, - * /]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="2">
|
|
||||||
周期从
|
|
||||||
<el-input-number v-model='cycle01' :min="1" :max="11" /> -
|
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="12" /> 月
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="3">
|
|
||||||
从
|
|
||||||
<el-input-number v-model='average01' :min="1" :max="11" /> 月开始,每
|
|
||||||
<el-input-number v-model='average02' :min="1" :max="12 - average01" /> 月月执行一次
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="4">
|
|
||||||
指定
|
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
|
||||||
<el-option v-for="item in monthList" :key="item.key" :label="item.value" :value="item.key" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const radioValue = ref(1)
|
|
||||||
const cycle01 = ref(1)
|
|
||||||
const cycle02 = ref(2)
|
|
||||||
const average01 = ref(1)
|
|
||||||
const average02 = ref(1)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([1])
|
|
||||||
const monthList = ref([
|
|
||||||
{key: 1, value: '一月'},
|
|
||||||
{key: 2, value: '二月'},
|
|
||||||
{key: 3, value: '三月'},
|
|
||||||
{key: 4, value: '四月'},
|
|
||||||
{key: 5, value: '五月'},
|
|
||||||
{key: 6, value: '六月'},
|
|
||||||
{key: 7, value: '七月'},
|
|
||||||
{key: 8, value: '八月'},
|
|
||||||
{key: 9, value: '九月'},
|
|
||||||
{key: 10, value: '十月'},
|
|
||||||
{key: 11, value: '十一月'},
|
|
||||||
{key: 12, value: '十二月'}
|
|
||||||
])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, 1, 11)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 12)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, 1, 11)
|
|
||||||
average02.value = props.check(average02.value, 1, 12 - average01.value)
|
|
||||||
return average01.value + '/' + average02.value
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.month, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (value === '*') {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value.indexOf('-') > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf('/') > -1) {
|
|
||||||
const indexArr = value.split('/')
|
|
||||||
average01.value = Number(indexArr[0])
|
|
||||||
average02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onRadioChange() {
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'month', '*', 'month')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'month', cycleTotal.value, 'month')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'month', averageTotal.value, 'month')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'month', checkboxString.value, 'month')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.2rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 18.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,540 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="popup-result">
|
|
||||||
<p class="title">最近5次运行时间</p>
|
|
||||||
<ul class="popup-result-scroll">
|
|
||||||
<template v-if='isShow'>
|
|
||||||
<li v-for='item in resultList' :key="item">{{item}}</li>
|
|
||||||
</template>
|
|
||||||
<li v-else>计算结果中...</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
ex: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const dayRule = ref('')
|
|
||||||
const dayRuleSup = ref('')
|
|
||||||
const dateArr = ref([])
|
|
||||||
const resultList = ref([])
|
|
||||||
const isShow = ref(false)
|
|
||||||
watch(() => props.ex, () => expressionChange())
|
|
||||||
// 表达式值变化时,开始去计算结果
|
|
||||||
function expressionChange() {
|
|
||||||
// 计算开始-隐藏结果
|
|
||||||
isShow.value = false;
|
|
||||||
// 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]
|
|
||||||
let ruleArr = props.ex.split(' ');
|
|
||||||
// 用于记录进入循环的次数
|
|
||||||
let nums = 0;
|
|
||||||
// 用于暂时存符号时间规则结果的数组
|
|
||||||
let resultArr = [];
|
|
||||||
// 获取当前时间精确至[年、月、日、时、分、秒]
|
|
||||||
let nTime = new Date();
|
|
||||||
let nYear = nTime.getFullYear();
|
|
||||||
let nMonth = nTime.getMonth() + 1;
|
|
||||||
let nDay = nTime.getDate();
|
|
||||||
let nHour = nTime.getHours();
|
|
||||||
let nMin = nTime.getMinutes();
|
|
||||||
let nSecond = nTime.getSeconds();
|
|
||||||
// 根据规则获取到近100年可能年数组、月数组等等
|
|
||||||
getSecondArr(ruleArr[0]);
|
|
||||||
getMinArr(ruleArr[1]);
|
|
||||||
getHourArr(ruleArr[2]);
|
|
||||||
getDayArr(ruleArr[3]);
|
|
||||||
getMonthArr(ruleArr[4]);
|
|
||||||
getWeekArr(ruleArr[5]);
|
|
||||||
getYearArr(ruleArr[6], nYear);
|
|
||||||
// 将获取到的数组赋值-方便使用
|
|
||||||
let sDate = dateArr.value[0];
|
|
||||||
let mDate = dateArr.value[1];
|
|
||||||
let hDate = dateArr.value[2];
|
|
||||||
let DDate = dateArr.value[3];
|
|
||||||
let MDate = dateArr.value[4];
|
|
||||||
let YDate = dateArr.value[5];
|
|
||||||
// 获取当前时间在数组中的索引
|
|
||||||
let sIdx = getIndex(sDate, nSecond);
|
|
||||||
let mIdx = getIndex(mDate, nMin);
|
|
||||||
let hIdx = getIndex(hDate, nHour);
|
|
||||||
let DIdx = getIndex(DDate, nDay);
|
|
||||||
let MIdx = getIndex(MDate, nMonth);
|
|
||||||
let YIdx = getIndex(YDate, nYear);
|
|
||||||
// 重置月日时分秒的函数(后面用的比较多)
|
|
||||||
const resetSecond = function () {
|
|
||||||
sIdx = 0;
|
|
||||||
nSecond = sDate[sIdx]
|
|
||||||
}
|
|
||||||
const resetMin = function () {
|
|
||||||
mIdx = 0;
|
|
||||||
nMin = mDate[mIdx]
|
|
||||||
resetSecond();
|
|
||||||
}
|
|
||||||
const resetHour = function () {
|
|
||||||
hIdx = 0;
|
|
||||||
nHour = hDate[hIdx]
|
|
||||||
resetMin();
|
|
||||||
}
|
|
||||||
const resetDay = function () {
|
|
||||||
DIdx = 0;
|
|
||||||
nDay = DDate[DIdx]
|
|
||||||
resetHour();
|
|
||||||
}
|
|
||||||
const resetMonth = function () {
|
|
||||||
MIdx = 0;
|
|
||||||
nMonth = MDate[MIdx]
|
|
||||||
resetDay();
|
|
||||||
}
|
|
||||||
// 如果当前年份不为数组中当前值
|
|
||||||
if (nYear !== YDate[YIdx]) {
|
|
||||||
resetMonth();
|
|
||||||
}
|
|
||||||
// 如果当前月份不为数组中当前值
|
|
||||||
if (nMonth !== MDate[MIdx]) {
|
|
||||||
resetDay();
|
|
||||||
}
|
|
||||||
// 如果当前“日”不为数组中当前值
|
|
||||||
if (nDay !== DDate[DIdx]) {
|
|
||||||
resetHour();
|
|
||||||
}
|
|
||||||
// 如果当前“时”不为数组中当前值
|
|
||||||
if (nHour !== hDate[hIdx]) {
|
|
||||||
resetMin();
|
|
||||||
}
|
|
||||||
// 如果当前“分”不为数组中当前值
|
|
||||||
if (nMin !== mDate[mIdx]) {
|
|
||||||
resetSecond();
|
|
||||||
}
|
|
||||||
// 循环年份数组
|
|
||||||
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
|
|
||||||
let YY = YDate[Yi];
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (nMonth > MDate[MDate.length - 1]) {
|
|
||||||
resetMonth();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 循环月份数组
|
|
||||||
goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
|
|
||||||
// 赋值、方便后面运算
|
|
||||||
let MM = MDate[Mi];
|
|
||||||
MM = MM < 10 ? '0' + MM : MM;
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (nDay > DDate[DDate.length - 1]) {
|
|
||||||
resetDay();
|
|
||||||
if (Mi === MDate.length - 1) {
|
|
||||||
resetMonth();
|
|
||||||
continue goYear;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 循环日期数组
|
|
||||||
goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
|
|
||||||
// 赋值、方便后面运算
|
|
||||||
let DD = DDate[Di];
|
|
||||||
let thisDD = DD < 10 ? '0' + DD : DD;
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (nHour > hDate[hDate.length - 1]) {
|
|
||||||
resetHour();
|
|
||||||
if (Di === DDate.length - 1) {
|
|
||||||
resetDay();
|
|
||||||
if (Mi === MDate.length - 1) {
|
|
||||||
resetMonth();
|
|
||||||
continue goYear;
|
|
||||||
}
|
|
||||||
continue goMonth;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 判断日期的合法性,不合法的话也是跳出当前循环
|
|
||||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && dayRule.value !== 'workDay' && dayRule.value !== 'lastWeek' && dayRule.value !== 'lastDay') {
|
|
||||||
resetDay();
|
|
||||||
continue goMonth;
|
|
||||||
}
|
|
||||||
// 如果日期规则中有值时
|
|
||||||
if (dayRule.value === 'lastDay') {
|
|
||||||
// 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天
|
|
||||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
DD--;
|
|
||||||
thisDD = DD < 10 ? '0' + DD : DD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (dayRule.value === 'workDay') {
|
|
||||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
|
||||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
DD--;
|
|
||||||
thisDD = DD < 10 ? '0' + DD : DD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取达到条件的日期是星期X
|
|
||||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
|
|
||||||
// 当星期日时
|
|
||||||
if (thisWeek === 1) {
|
|
||||||
// 先找下一个日,并判断是否为月底
|
|
||||||
DD++;
|
|
||||||
thisDD = DD < 10 ? '0' + DD : DD;
|
|
||||||
// 判断下一日已经不是合法日期
|
|
||||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
DD -= 3;
|
|
||||||
}
|
|
||||||
} else if (thisWeek === 7) {
|
|
||||||
// 当星期6时只需判断不是1号就可进行操作
|
|
||||||
if (dayRuleSup.value !== 1) {
|
|
||||||
DD--;
|
|
||||||
} else {
|
|
||||||
DD += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (dayRule.value === 'weekDay') {
|
|
||||||
// 如果指定了是星期几
|
|
||||||
// 获取当前日期是属于星期几
|
|
||||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
|
|
||||||
// 校验当前星期是否在星期池(dayRuleSup)中
|
|
||||||
if (dayRuleSup.value.indexOf(thisWeek) < 0) {
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (Di === DDate.length - 1) {
|
|
||||||
resetDay();
|
|
||||||
if (Mi === MDate.length - 1) {
|
|
||||||
resetMonth();
|
|
||||||
continue goYear;
|
|
||||||
}
|
|
||||||
continue goMonth;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (dayRule.value === 'assWeek') {
|
|
||||||
// 如果指定了是第几周的星期几
|
|
||||||
// 获取每月1号是属于星期几
|
|
||||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
|
|
||||||
if (dayRuleSup.value[1] >= thisWeek) {
|
|
||||||
DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1;
|
|
||||||
} else {
|
|
||||||
DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1;
|
|
||||||
}
|
|
||||||
} else if (dayRule.value === 'lastWeek') {
|
|
||||||
// 如果指定了每月最后一个星期几
|
|
||||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
|
||||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
|
||||||
DD--;
|
|
||||||
thisDD = DD < 10 ? '0' + DD : DD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取月末最后一天是星期几
|
|
||||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
|
|
||||||
// 找到要求中最近的那个星期几
|
|
||||||
if (dayRuleSup.value < thisWeek) {
|
|
||||||
DD -= thisWeek - dayRuleSup.value;
|
|
||||||
} else if (dayRuleSup.value > thisWeek) {
|
|
||||||
DD -= 7 - (dayRuleSup.value - thisWeek)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 判断时间值是否小于10置换成“05”这种格式
|
|
||||||
DD = DD < 10 ? '0' + DD : DD;
|
|
||||||
// 循环“时”数组
|
|
||||||
goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
|
|
||||||
let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (nMin > mDate[mDate.length - 1]) {
|
|
||||||
resetMin();
|
|
||||||
if (hi === hDate.length - 1) {
|
|
||||||
resetHour();
|
|
||||||
if (Di === DDate.length - 1) {
|
|
||||||
resetDay();
|
|
||||||
if (Mi === MDate.length - 1) {
|
|
||||||
resetMonth();
|
|
||||||
continue goYear;
|
|
||||||
}
|
|
||||||
continue goMonth;
|
|
||||||
}
|
|
||||||
continue goDay;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 循环"分"数组
|
|
||||||
goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
|
|
||||||
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (nSecond > sDate[sDate.length - 1]) {
|
|
||||||
resetSecond();
|
|
||||||
if (mi === mDate.length - 1) {
|
|
||||||
resetMin();
|
|
||||||
if (hi === hDate.length - 1) {
|
|
||||||
resetHour();
|
|
||||||
if (Di === DDate.length - 1) {
|
|
||||||
resetDay();
|
|
||||||
if (Mi === MDate.length - 1) {
|
|
||||||
resetMonth();
|
|
||||||
continue goYear;
|
|
||||||
}
|
|
||||||
continue goMonth;
|
|
||||||
}
|
|
||||||
continue goDay;
|
|
||||||
}
|
|
||||||
continue goHour;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 循环"秒"数组
|
|
||||||
goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
|
|
||||||
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];
|
|
||||||
// 添加当前时间(时间合法性在日期循环时已经判断)
|
|
||||||
if (MM !== '00' && DD !== '00') {
|
|
||||||
resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
|
|
||||||
nums++;
|
|
||||||
}
|
|
||||||
// 如果条数满了就退出循环
|
|
||||||
if (nums === 5) break goYear;
|
|
||||||
// 如果到达最大值时
|
|
||||||
if (si === sDate.length - 1) {
|
|
||||||
resetSecond();
|
|
||||||
if (mi === mDate.length - 1) {
|
|
||||||
resetMin();
|
|
||||||
if (hi === hDate.length - 1) {
|
|
||||||
resetHour();
|
|
||||||
if (Di === DDate.length - 1) {
|
|
||||||
resetDay();
|
|
||||||
if (Mi === MDate.length - 1) {
|
|
||||||
resetMonth();
|
|
||||||
continue goYear;
|
|
||||||
}
|
|
||||||
continue goMonth;
|
|
||||||
}
|
|
||||||
continue goDay;
|
|
||||||
}
|
|
||||||
continue goHour;
|
|
||||||
}
|
|
||||||
continue goMin;
|
|
||||||
}
|
|
||||||
} //goSecond
|
|
||||||
} //goMin
|
|
||||||
}//goHour
|
|
||||||
}//goDay
|
|
||||||
}//goMonth
|
|
||||||
}
|
|
||||||
// 判断100年内的结果条数
|
|
||||||
if (resultArr.length === 0) {
|
|
||||||
resultList.value = ['没有达到条件的结果!'];
|
|
||||||
} else {
|
|
||||||
resultList.value = resultArr;
|
|
||||||
if (resultArr.length !== 5) {
|
|
||||||
resultList.value.push('最近100年内只有上面' + resultArr.length + '条结果!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 计算完成-显示结果
|
|
||||||
isShow.value = true;
|
|
||||||
}
|
|
||||||
// 用于计算某位数字在数组中的索引
|
|
||||||
function getIndex(arr, value) {
|
|
||||||
if (value <= arr[0] || value > arr[arr.length - 1]) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < arr.length - 1; i++) {
|
|
||||||
if (value > arr[i] && value <= arr[i + 1]) {
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"年"数组
|
|
||||||
function getYearArr(rule, year) {
|
|
||||||
dateArr.value[5] = getOrderArr(year, year + 100);
|
|
||||||
if (rule !== undefined) {
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dateArr.value[5] = getCycleArr(rule, year + 100, false)
|
|
||||||
} else if (rule.indexOf('/') >= 0) {
|
|
||||||
dateArr.value[5] = getAverageArr(rule, year + 100)
|
|
||||||
} else if (rule !== '*') {
|
|
||||||
dateArr.value[5] = getAssignArr(rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"月"数组
|
|
||||||
function getMonthArr(rule) {
|
|
||||||
dateArr.value[4] = getOrderArr(1, 12);
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dateArr.value[4] = getCycleArr(rule, 12, false)
|
|
||||||
} else if (rule.indexOf('/') >= 0) {
|
|
||||||
dateArr.value[4] = getAverageArr(rule, 12)
|
|
||||||
} else if (rule !== '*') {
|
|
||||||
dateArr.value[4] = getAssignArr(rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"日"数组-主要为日期规则
|
|
||||||
function getWeekArr(rule) {
|
|
||||||
// 只有当日期规则的两个值均为“”时则表达日期是有选项的
|
|
||||||
if (dayRule.value === '' && dayRuleSup.value === '') {
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dayRule.value = 'weekDay';
|
|
||||||
dayRuleSup.value = getCycleArr(rule, 7, false)
|
|
||||||
} else if (rule.indexOf('#') >= 0) {
|
|
||||||
dayRule.value = 'assWeek';
|
|
||||||
let matchRule = rule.match(/[0-9]{1}/g);
|
|
||||||
dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])];
|
|
||||||
dateArr.value[3] = [1];
|
|
||||||
if (dayRuleSup.value[1] === 7) {
|
|
||||||
dayRuleSup.value[1] = 0;
|
|
||||||
}
|
|
||||||
} else if (rule.indexOf('L') >= 0) {
|
|
||||||
dayRule.value = 'lastWeek';
|
|
||||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]);
|
|
||||||
dateArr.value[3] = [31];
|
|
||||||
if (dayRuleSup.value === 7) {
|
|
||||||
dayRuleSup.value = 0;
|
|
||||||
}
|
|
||||||
} else if (rule !== '*' && rule !== '?') {
|
|
||||||
dayRule.value = 'weekDay';
|
|
||||||
dayRuleSup.value = getAssignArr(rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"日"数组-少量为日期规则
|
|
||||||
function getDayArr(rule) {
|
|
||||||
dateArr.value[3] = getOrderArr(1, 31);
|
|
||||||
dayRule.value = '';
|
|
||||||
dayRuleSup.value = '';
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dateArr.value[3] = getCycleArr(rule, 31, false)
|
|
||||||
dayRuleSup.value = 'null';
|
|
||||||
} else if (rule.indexOf('/') >= 0) {
|
|
||||||
dateArr.value[3] = getAverageArr(rule, 31)
|
|
||||||
dayRuleSup.value = 'null';
|
|
||||||
} else if (rule.indexOf('W') >= 0) {
|
|
||||||
dayRule.value = 'workDay';
|
|
||||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]);
|
|
||||||
dateArr.value[3] = [dayRuleSup.value];
|
|
||||||
} else if (rule.indexOf('L') >= 0) {
|
|
||||||
dayRule.value = 'lastDay';
|
|
||||||
dayRuleSup.value = 'null';
|
|
||||||
dateArr.value[3] = [31];
|
|
||||||
} else if (rule !== '*' && rule !== '?') {
|
|
||||||
dateArr.value[3] = getAssignArr(rule)
|
|
||||||
dayRuleSup.value = 'null';
|
|
||||||
} else if (rule === '*') {
|
|
||||||
dayRuleSup.value = 'null';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"时"数组
|
|
||||||
function getHourArr(rule) {
|
|
||||||
dateArr.value[2] = getOrderArr(0, 23);
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dateArr.value[2] = getCycleArr(rule, 24, true)
|
|
||||||
} else if (rule.indexOf('/') >= 0) {
|
|
||||||
dateArr.value[2] = getAverageArr(rule, 23)
|
|
||||||
} else if (rule !== '*') {
|
|
||||||
dateArr.value[2] = getAssignArr(rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"分"数组
|
|
||||||
function getMinArr(rule) {
|
|
||||||
dateArr.value[1] = getOrderArr(0, 59);
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dateArr.value[1] = getCycleArr(rule, 60, true)
|
|
||||||
} else if (rule.indexOf('/') >= 0) {
|
|
||||||
dateArr.value[1] = getAverageArr(rule, 59)
|
|
||||||
} else if (rule !== '*') {
|
|
||||||
dateArr.value[1] = getAssignArr(rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取"秒"数组
|
|
||||||
function getSecondArr(rule) {
|
|
||||||
dateArr.value[0] = getOrderArr(0, 59);
|
|
||||||
if (rule.indexOf('-') >= 0) {
|
|
||||||
dateArr.value[0] = getCycleArr(rule, 60, true)
|
|
||||||
} else if (rule.indexOf('/') >= 0) {
|
|
||||||
dateArr.value[0] = getAverageArr(rule, 59)
|
|
||||||
} else if (rule !== '*') {
|
|
||||||
dateArr.value[0] = getAssignArr(rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 根据传进来的min-max返回一个顺序的数组
|
|
||||||
function getOrderArr(min, max) {
|
|
||||||
let arr = [];
|
|
||||||
for (let i = min; i <= max; i++) {
|
|
||||||
arr.push(i);
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
// 根据规则中指定的零散值返回一个数组
|
|
||||||
function getAssignArr(rule) {
|
|
||||||
let arr = [];
|
|
||||||
let assiginArr = rule.split(',');
|
|
||||||
for (let i = 0; i < assiginArr.length; i++) {
|
|
||||||
arr[i] = Number(assiginArr[i])
|
|
||||||
}
|
|
||||||
arr.sort(compare)
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
// 根据一定算术规则计算返回一个数组
|
|
||||||
function getAverageArr(rule, limit) {
|
|
||||||
let arr = [];
|
|
||||||
let agArr = rule.split('/');
|
|
||||||
let min = Number(agArr[0]);
|
|
||||||
let step = Number(agArr[1]);
|
|
||||||
while (min <= limit) {
|
|
||||||
arr.push(min);
|
|
||||||
min += step;
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
// 根据规则返回一个具有周期性的数组
|
|
||||||
function getCycleArr(rule, limit, status) {
|
|
||||||
// status--表示是否从0开始(则从1开始)
|
|
||||||
let arr = [];
|
|
||||||
let cycleArr = rule.split('-');
|
|
||||||
let min = Number(cycleArr[0]);
|
|
||||||
let max = Number(cycleArr[1]);
|
|
||||||
if (min > max) {
|
|
||||||
max += limit;
|
|
||||||
}
|
|
||||||
for (let i = min; i <= max; i++) {
|
|
||||||
let add = 0;
|
|
||||||
if (status === false && i % limit === 0) {
|
|
||||||
add = limit;
|
|
||||||
}
|
|
||||||
arr.push(Math.round(i % limit + add))
|
|
||||||
}
|
|
||||||
arr.sort(compare)
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
// 比较数字大小(用于Array.sort)
|
|
||||||
function compare(value1, value2) {
|
|
||||||
if (value2 - value1 > 0) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 格式化日期格式如:2017-9-19 18:04:33
|
|
||||||
function formatDate(value, type) {
|
|
||||||
// 计算日期相关值
|
|
||||||
let time = typeof value == 'number' ? new Date(value) : value;
|
|
||||||
let Y = time.getFullYear();
|
|
||||||
let M = time.getMonth() + 1;
|
|
||||||
let D = time.getDate();
|
|
||||||
let h = time.getHours();
|
|
||||||
let m = time.getMinutes();
|
|
||||||
let s = time.getSeconds();
|
|
||||||
let week = time.getDay();
|
|
||||||
// 如果传递了type的话
|
|
||||||
if (type === undefined) {
|
|
||||||
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
|
||||||
} else if (type === 'week') {
|
|
||||||
// 在quartz中 1为星期日
|
|
||||||
return week + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 检查日期是否存在
|
|
||||||
function checkDate(value) {
|
|
||||||
let time = new Date(value);
|
|
||||||
let format = formatDate(time)
|
|
||||||
return value === format;
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
expressionChange()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="1">
|
|
||||||
秒,允许的通配符[, - * /]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="2">
|
|
||||||
周期从
|
|
||||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 秒
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="3">
|
|
||||||
从
|
|
||||||
<el-input-number v-model='average01' :min="0" :max="58" /> 秒开始,每
|
|
||||||
<el-input-number v-model='average02' :min="1" :max="59 - average01" /> 秒执行一次
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="4">
|
|
||||||
指定
|
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
|
||||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: "",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const radioValue = ref(1)
|
|
||||||
const cycle01 = ref(0)
|
|
||||||
const cycle02 = ref(1)
|
|
||||||
const average01 = ref(0)
|
|
||||||
const average02 = ref(1)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([0])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, 0, 58)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, 0, 58)
|
|
||||||
average02.value = props.check(average02.value, 1, 59 - average01.value)
|
|
||||||
return average01.value + '/' + average02.value
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.second, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (value === '*') {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value.indexOf('-') > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf('/') > -1) {
|
|
||||||
const indexArr = value.split('/')
|
|
||||||
average01.value = Number(indexArr[0])
|
|
||||||
average02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 单选按钮值变化时
|
|
||||||
function onRadioChange() {
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'second', '*', 'second')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'second', cycleTotal.value, 'second')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'second', averageTotal.value, 'second')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'second', checkboxString.value, 'second')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.2rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 18.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,197 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="1">
|
|
||||||
周,允许的通配符[, - * ? / L #]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="2">
|
|
||||||
不指定
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="3">
|
|
||||||
周期从
|
|
||||||
<el-select clearable v-model="cycle01">
|
|
||||||
<el-option
|
|
||||||
v-for="(item,index) of weekList"
|
|
||||||
:key="index"
|
|
||||||
:label="item.value"
|
|
||||||
:value="item.key"
|
|
||||||
:disabled="item.key === 7"
|
|
||||||
>{{item.value}}</el-option>
|
|
||||||
</el-select>
|
|
||||||
-
|
|
||||||
<el-select clearable v-model="cycle02">
|
|
||||||
<el-option
|
|
||||||
v-for="(item,index) of weekList"
|
|
||||||
:key="index"
|
|
||||||
:label="item.value"
|
|
||||||
:value="item.key"
|
|
||||||
:disabled="item.key <= cycle01"
|
|
||||||
>{{item.value}}</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="4">
|
|
||||||
第
|
|
||||||
<el-input-number v-model='average01' :min="1" :max="4" /> 周的
|
|
||||||
<el-select clearable v-model="average02">
|
|
||||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="5">
|
|
||||||
本月最后一个
|
|
||||||
<el-select clearable v-model="weekday">
|
|
||||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio v-model='radioValue' :value="6">
|
|
||||||
指定
|
|
||||||
<el-select class="multiselect" clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="6">
|
|
||||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const radioValue = ref(2)
|
|
||||||
const cycle01 = ref(2)
|
|
||||||
const cycle02 = ref(3)
|
|
||||||
const average01 = ref(1)
|
|
||||||
const average02 = ref(2)
|
|
||||||
const weekday = ref(2)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([2])
|
|
||||||
const weekList = ref([
|
|
||||||
{key: 1, value: '星期日'},
|
|
||||||
{key: 2, value: '星期一'},
|
|
||||||
{key: 3, value: '星期二'},
|
|
||||||
{key: 4, value: '星期三'},
|
|
||||||
{key: 5, value: '星期四'},
|
|
||||||
{key: 6, value: '星期五'},
|
|
||||||
{key: 7, value: '星期六'}
|
|
||||||
])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, 1, 6)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 7)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, 1, 4)
|
|
||||||
average02.value = props.check(average02.value, 1, 7)
|
|
||||||
return average02.value + '#' + average01.value
|
|
||||||
})
|
|
||||||
const weekdayTotal = computed(() => {
|
|
||||||
weekday.value = props.check(weekday.value, 1, 7)
|
|
||||||
return weekday.value + 'L'
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.week, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, weekdayTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (value === "*") {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value === "?") {
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf("-") > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else if (value.indexOf("#") > -1) {
|
|
||||||
const indexArr = value.split('#')
|
|
||||||
average01.value = Number(indexArr[1])
|
|
||||||
average02.value = Number(indexArr[0])
|
|
||||||
radioValue.value = 4
|
|
||||||
} else if (value.indexOf("L") > -1) {
|
|
||||||
const indexArr = value.split("L")
|
|
||||||
weekday.value = Number(indexArr[0])
|
|
||||||
radioValue.value = 5
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onRadioChange() {
|
|
||||||
if (radioValue.value === 2 && props.cron.day === '?') {
|
|
||||||
emit('update', 'day', '*', 'week')
|
|
||||||
}
|
|
||||||
if (radioValue.value !== 2 && props.cron.day !== '?') {
|
|
||||||
emit('update', 'day', '?', 'week')
|
|
||||||
}
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'week', '*', 'week')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'week', '?', 'week')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'week', cycleTotal.value, 'week')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
emit('update', 'week', averageTotal.value, 'week')
|
|
||||||
break
|
|
||||||
case 5:
|
|
||||||
emit('update', 'week', weekdayTotal.value, 'week')
|
|
||||||
break
|
|
||||||
case 6:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'week', checkboxString.value, 'week')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.5rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 8rem;
|
|
||||||
}
|
|
||||||
.el-select.multiselect, .el-select--small.multiselect {
|
|
||||||
width: 17.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,149 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-form>
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio :value="1" v-model='radioValue'>
|
|
||||||
不填,允许的通配符[, - * /]
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio :value="2" v-model='radioValue'>
|
|
||||||
每年
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio :value="3" v-model='radioValue'>
|
|
||||||
周期从
|
|
||||||
<el-input-number v-model='cycle01' :min='fullYear' :max="2098"/> -
|
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : fullYear + 1" :max="2099"/>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio :value="4" v-model='radioValue'>
|
|
||||||
从
|
|
||||||
<el-input-number v-model='average01' :min='fullYear' :max="2098"/> 年开始,每
|
|
||||||
<el-input-number v-model='average02' :min="1" :max="2099 - average01 || fullYear"/> 年执行一次
|
|
||||||
</el-radio>
|
|
||||||
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-radio :value="5" v-model='radioValue'>
|
|
||||||
指定
|
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
|
||||||
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" />
|
|
||||||
</el-select>
|
|
||||||
</el-radio>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const emit = defineEmits(['update'])
|
|
||||||
const props = defineProps({
|
|
||||||
cron: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
second: "*",
|
|
||||||
min: "*",
|
|
||||||
hour: "*",
|
|
||||||
day: "*",
|
|
||||||
month: "*",
|
|
||||||
week: "?",
|
|
||||||
year: ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
check: {
|
|
||||||
type: Function,
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const fullYear = ref(0)
|
|
||||||
const maxFullYear = ref(0)
|
|
||||||
const radioValue = ref(1)
|
|
||||||
const cycle01 = ref(0)
|
|
||||||
const cycle02 = ref(0)
|
|
||||||
const average01 = ref(0)
|
|
||||||
const average02 = ref(1)
|
|
||||||
const checkboxList = ref([])
|
|
||||||
const checkCopy = ref([])
|
|
||||||
const cycleTotal = computed(() => {
|
|
||||||
cycle01.value = props.check(cycle01.value, fullYear.value, maxFullYear.value - 1)
|
|
||||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, maxFullYear.value)
|
|
||||||
return cycle01.value + '-' + cycle02.value
|
|
||||||
})
|
|
||||||
const averageTotal = computed(() => {
|
|
||||||
average01.value = props.check(average01.value, fullYear.value, maxFullYear.value - 1)
|
|
||||||
average02.value = props.check(average02.value, 1, 10)
|
|
||||||
return average01.value + '/' + average02.value
|
|
||||||
})
|
|
||||||
const checkboxString = computed(() => {
|
|
||||||
return checkboxList.value.join(',')
|
|
||||||
})
|
|
||||||
watch(() => props.cron.year, value => changeRadioValue(value))
|
|
||||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
|
||||||
function changeRadioValue(value) {
|
|
||||||
if (value === '') {
|
|
||||||
radioValue.value = 1
|
|
||||||
} else if (value === "*") {
|
|
||||||
radioValue.value = 2
|
|
||||||
} else if (value.indexOf("-") > -1) {
|
|
||||||
const indexArr = value.split('-')
|
|
||||||
cycle01.value = Number(indexArr[0])
|
|
||||||
cycle02.value = Number(indexArr[1])
|
|
||||||
radioValue.value = 3
|
|
||||||
} else if (value.indexOf("/") > -1) {
|
|
||||||
const indexArr = value.split('/')
|
|
||||||
average01.value = Number(indexArr[1])
|
|
||||||
average02.value = Number(indexArr[0])
|
|
||||||
radioValue.value = 4
|
|
||||||
} else {
|
|
||||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
|
||||||
radioValue.value = 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onRadioChange() {
|
|
||||||
switch (radioValue.value) {
|
|
||||||
case 1:
|
|
||||||
emit('update', 'year', '', 'year')
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
emit('update', 'year', '*', 'year')
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
emit('update', 'year', cycleTotal.value, 'year')
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
emit('update', 'year', averageTotal.value, 'year')
|
|
||||||
break
|
|
||||||
case 5:
|
|
||||||
if (checkboxList.value.length === 0) {
|
|
||||||
checkboxList.value.push(checkCopy.value[0])
|
|
||||||
} else {
|
|
||||||
checkCopy.value = checkboxList.value
|
|
||||||
}
|
|
||||||
emit('update', 'year', checkboxString.value, 'year')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onMounted(() => {
|
|
||||||
fullYear.value = Number(new Date().getFullYear())
|
|
||||||
maxFullYear.value = fullYear.value + 10
|
|
||||||
cycle01.value = fullYear.value
|
|
||||||
cycle02.value = cycle01.value + 1
|
|
||||||
average01.value = fullYear.value
|
|
||||||
checkCopy.value = [fullYear.value]
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-input-number--small, .el-select, .el-select--small {
|
|
||||||
margin: 0 0.2rem;
|
|
||||||
}
|
|
||||||
.el-select, .el-select--small {
|
|
||||||
width: 18.8rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="dict-container">
|
|
||||||
<template v-for="(item, index) in options">
|
|
||||||
<template v-if="values.includes(item.value)">
|
|
||||||
<span
|
|
||||||
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
|
|
||||||
:key="item.value"
|
|
||||||
:index="index"
|
|
||||||
:class="item.elTagClass"
|
|
||||||
>{{ item.label + " " }}</span>
|
|
||||||
<el-tag
|
|
||||||
v-else
|
|
||||||
:disable-transitions="true"
|
|
||||||
:key="item.value + ''"
|
|
||||||
:index="index"
|
|
||||||
:type="item.elTagType"
|
|
||||||
:class="item.elTagClass"
|
|
||||||
>{{ item.label + " " }}</el-tag>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<template v-if="unmatch && showValue">
|
|
||||||
{{ unmatchArray | handleArray }}
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
// 记录未匹配的项
|
|
||||||
const unmatchArray = ref([]);
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
// 数据
|
|
||||||
options: {
|
|
||||||
type: Array,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
// 当前的值
|
|
||||||
value: [Number, String, Array],
|
|
||||||
// 当未找到匹配的数据时,显示value
|
|
||||||
showValue: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
separator: {
|
|
||||||
type: String,
|
|
||||||
default: ",",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const values = computed(() => {
|
|
||||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '') return [];
|
|
||||||
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
|
|
||||||
});
|
|
||||||
|
|
||||||
const unmatch = computed(() => {
|
|
||||||
unmatchArray.value = [];
|
|
||||||
// 没有value不显示
|
|
||||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '' || !Array.isArray(props.options) || props.options.length === 0) return false
|
|
||||||
// 传入值为数组
|
|
||||||
let unmatch = false // 添加一个标志来判断是否有未匹配项
|
|
||||||
values.value.forEach(item => {
|
|
||||||
if (!props.options.some(v => v.value === item)) {
|
|
||||||
unmatchArray.value.push(item)
|
|
||||||
unmatch = true // 如果有未匹配项,将标志设置为true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return unmatch // 返回标志的值
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleArray(array) {
|
|
||||||
if (array.length === 0) return "";
|
|
||||||
return array.reduce((pre, cur) => {
|
|
||||||
return pre + " " + cur;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-tag + .el-tag {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
// .dict-container {
|
|
||||||
// &:deep(.el-tag) {
|
|
||||||
// background-color: transparent;
|
|
||||||
// border: 0;
|
|
||||||
// font-size: 14px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
<template>
|
|
||||||
<div id="echarts" ref="chartRef" :style="echartsStyle" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup name="ECharts">
|
|
||||||
import { ref, onMounted, onBeforeUnmount, watch, computed, markRaw, nextTick, onActivated } from "vue";
|
|
||||||
import * as echarts from "echarts";
|
|
||||||
import { useDebounceFn } from "@vueuse/core";
|
|
||||||
import useAppStore from '@/store/modules/app'
|
|
||||||
const appStore = useAppStore()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
option: {
|
|
||||||
type: Object,
|
|
||||||
default: {},
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
renderer: {
|
|
||||||
type: String,
|
|
||||||
default: "canvas" || "svg"
|
|
||||||
},
|
|
||||||
resize: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: Object || String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: Number || String,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number || String,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
onClick: {
|
|
||||||
type: Function,
|
|
||||||
default: (event) => any,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const echartsStyle = computed(() => {
|
|
||||||
return props.width || props.height
|
|
||||||
? { height: props.height + "px", width: props.width + "px" }
|
|
||||||
: { height: "100%", width: "100%" };
|
|
||||||
});
|
|
||||||
|
|
||||||
const chartRef = ref(null);
|
|
||||||
const chartInstance = ref();
|
|
||||||
|
|
||||||
const draw = () => {
|
|
||||||
if (chartInstance.value) {
|
|
||||||
chartInstance.value.setOption(props.option, { notMerge: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(props, () => {
|
|
||||||
draw();
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleClick = (event) => props.onClick && props.onClick(event);
|
|
||||||
|
|
||||||
const init = () => {
|
|
||||||
if (!chartRef.value) return;
|
|
||||||
chartInstance.value = echarts.getInstanceByDom(chartRef.value);
|
|
||||||
|
|
||||||
if (!chartInstance.value) {
|
|
||||||
chartInstance.value = markRaw(
|
|
||||||
echarts.init(chartRef.value, props.theme, {
|
|
||||||
renderer: props.renderer
|
|
||||||
})
|
|
||||||
);
|
|
||||||
chartInstance.value.on("click", handleClick);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const resize = () => {
|
|
||||||
if (chartInstance.value && props.resize) {
|
|
||||||
chartInstance.value.resize({ animation: { duration: 300 } });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const debouncedResize = useDebounceFn(resize, 300, { maxWait: 800 });
|
|
||||||
|
|
||||||
|
|
||||||
// 左侧菜单收起/打开时,重置echarts的宽高
|
|
||||||
const isCollapse = computed(() => !appStore.sidebar.opened);
|
|
||||||
watch(
|
|
||||||
() => [isCollapse],
|
|
||||||
() => {
|
|
||||||
debouncedResize();
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
nextTick(() => init());
|
|
||||||
window.addEventListener("resize", debouncedResize);
|
|
||||||
});
|
|
||||||
|
|
||||||
onActivated(() => {
|
|
||||||
if (chartInstance.value) {
|
|
||||||
chartInstance.value.resize();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
chartInstance.value?.dispose();
|
|
||||||
window.removeEventListener("resize", debouncedResize);
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
getInstance: () => chartInstance.value,
|
|
||||||
resize,
|
|
||||||
draw
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,251 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-upload
|
|
||||||
:action="uploadUrl"
|
|
||||||
:before-upload="handleBeforeUpload"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
:on-error="handleUploadError"
|
|
||||||
name="file"
|
|
||||||
:show-file-list="false"
|
|
||||||
:headers="headers"
|
|
||||||
class="editor-img-uploader"
|
|
||||||
v-if="type == 'url'"
|
|
||||||
>
|
|
||||||
<i ref="uploadRef" class="editor-img-uploader"></i>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div class="editor">
|
|
||||||
<quill-editor
|
|
||||||
ref="quillEditorRef"
|
|
||||||
v-model:content="content"
|
|
||||||
contentType="html"
|
|
||||||
@textChange="(e) => $emit('update:modelValue', content)"
|
|
||||||
:options="options"
|
|
||||||
:style="styles"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { QuillEditor } from "@vueup/vue-quill";
|
|
||||||
import "@vueup/vue-quill/dist/vue-quill.snow.css";
|
|
||||||
import { getToken } from "@/utils/auth";
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
|
|
||||||
const quillEditorRef = ref();
|
|
||||||
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
|
||||||
const headers = ref({
|
|
||||||
Authorization: "Bearer " + getToken()
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
/* 编辑器的内容 */
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
/* 高度 */
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
/* 最小高度 */
|
|
||||||
minHeight: {
|
|
||||||
type: Number,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
/* 只读 */
|
|
||||||
readOnly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
/* 上传文件大小限制(MB) */
|
|
||||||
fileSize: {
|
|
||||||
type: Number,
|
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
/* 类型(base64格式、url格式) */
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: "url",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const options = ref({
|
|
||||||
theme: "snow",
|
|
||||||
bounds: document.body,
|
|
||||||
debug: "warn",
|
|
||||||
modules: {
|
|
||||||
// 工具栏配置
|
|
||||||
toolbar: [
|
|
||||||
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
|
|
||||||
["blockquote", "code-block"], // 引用 代码块
|
|
||||||
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
|
|
||||||
[{ indent: "-1" }, { indent: "+1" }], // 缩进
|
|
||||||
[{ size: ["small", false, "large", "huge"] }], // 字体大小
|
|
||||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
|
||||||
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
|
||||||
[{ align: [] }], // 对齐方式
|
|
||||||
["clean"], // 清除文本格式
|
|
||||||
["link", "image", "video"] // 链接、图片、视频
|
|
||||||
],
|
|
||||||
},
|
|
||||||
placeholder: "请输入内容",
|
|
||||||
readOnly: props.readOnly
|
|
||||||
});
|
|
||||||
|
|
||||||
const styles = computed(() => {
|
|
||||||
let style = {};
|
|
||||||
if (props.minHeight) {
|
|
||||||
style.minHeight = `${props.minHeight}px`;
|
|
||||||
}
|
|
||||||
if (props.height) {
|
|
||||||
style.height = `${props.height}px`;
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
});
|
|
||||||
|
|
||||||
const content = ref("");
|
|
||||||
watch(() => props.modelValue, (v) => {
|
|
||||||
if (v !== content.value) {
|
|
||||||
content.value = v == undefined ? "<p></p>" : v;
|
|
||||||
}
|
|
||||||
}, { immediate: true });
|
|
||||||
|
|
||||||
// 如果设置了上传地址则自定义图片上传事件
|
|
||||||
onMounted(() => {
|
|
||||||
if (props.type == 'url') {
|
|
||||||
let quill = quillEditorRef.value.getQuill();
|
|
||||||
let toolbar = quill.getModule("toolbar");
|
|
||||||
toolbar.addHandler("image", (value) => {
|
|
||||||
if (value) {
|
|
||||||
proxy.$refs.uploadRef.click();
|
|
||||||
} else {
|
|
||||||
quill.format("image", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 上传前校检格式和大小
|
|
||||||
function handleBeforeUpload(file) {
|
|
||||||
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
|
|
||||||
const isJPG = type.includes(file.type);
|
|
||||||
//检验文件格式
|
|
||||||
if (!isJPG) {
|
|
||||||
proxy.$modal.msgError(`图片格式错误!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 校检文件大小
|
|
||||||
if (props.fileSize) {
|
|
||||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
|
||||||
if (!isLt) {
|
|
||||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传成功处理
|
|
||||||
function handleUploadSuccess(res, file) {
|
|
||||||
// 如果上传成功
|
|
||||||
if (res.code == 200) {
|
|
||||||
// 获取富文本实例
|
|
||||||
let quill = toRaw(quillEditorRef.value).getQuill();
|
|
||||||
// 获取光标位置
|
|
||||||
let length = quill.selection.savedRange.index;
|
|
||||||
// 插入图片,res.url为服务器返回的图片链接地址
|
|
||||||
quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName);
|
|
||||||
// 调整光标到最后
|
|
||||||
quill.setSelection(length + 1);
|
|
||||||
} else {
|
|
||||||
proxy.$modal.msgError("图片插入失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传失败处理
|
|
||||||
function handleUploadError() {
|
|
||||||
proxy.$modal.msgError("图片插入失败");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.editor-img-uploader {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.editor, .ql-toolbar {
|
|
||||||
white-space: pre-wrap !important;
|
|
||||||
line-height: normal !important;
|
|
||||||
}
|
|
||||||
.quill-img {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
|
||||||
content: "请输入链接地址:";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
|
||||||
border-right: 0px;
|
|
||||||
content: "保存";
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
|
||||||
content: "请输入视频地址:";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
|
||||||
content: "14px";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
|
||||||
content: "10px";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
|
||||||
content: "18px";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
|
||||||
content: "32px";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
|
||||||
content: "文本";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
|
||||||
content: "标题1";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
|
||||||
content: "标题2";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
|
||||||
content: "标题3";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
|
||||||
content: "标题4";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
|
||||||
content: "标题5";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
|
||||||
content: "标题6";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
|
||||||
content: "标准字体";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
|
||||||
content: "衬线字体";
|
|
||||||
}
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
|
||||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
|
||||||
content: "等宽字体";
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="upload-file">
|
|
||||||
<el-upload
|
|
||||||
multiple
|
|
||||||
:action="uploadFileUrl"
|
|
||||||
:before-upload="handleBeforeUpload"
|
|
||||||
:file-list="fileList"
|
|
||||||
:limit="limit"
|
|
||||||
:on-error="handleUploadError"
|
|
||||||
:on-exceed="handleExceed"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
:show-file-list="false"
|
|
||||||
:headers="headers"
|
|
||||||
class="upload-file-uploader"
|
|
||||||
ref="fileUpload"
|
|
||||||
v-if="!disabled"
|
|
||||||
>
|
|
||||||
<!-- 上传按钮 -->
|
|
||||||
<el-button type="primary">选取文件</el-button>
|
|
||||||
</el-upload>
|
|
||||||
<!-- 上传提示 -->
|
|
||||||
<div class="el-upload__tip" v-if="showTip && !disabled">
|
|
||||||
请上传
|
|
||||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
|
||||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
|
||||||
的文件
|
|
||||||
</div>
|
|
||||||
<!-- 文件列表 -->
|
|
||||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
|
||||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
|
||||||
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
|
||||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
|
||||||
</el-link>
|
|
||||||
<div class="ele-upload-list__item-content-action">
|
|
||||||
<el-link :underline="false" @click="handleDelete(index)" type="danger" v-if="!disabled">删除</el-link>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</transition-group>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { getToken } from "@/utils/auth";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: [String, Object, Array],
|
|
||||||
// 数量限制
|
|
||||||
limit: {
|
|
||||||
type: Number,
|
|
||||||
default: 5
|
|
||||||
},
|
|
||||||
// 大小限制(MB)
|
|
||||||
fileSize: {
|
|
||||||
type: Number,
|
|
||||||
default: 5
|
|
||||||
},
|
|
||||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
|
||||||
fileType: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "pdf"]
|
|
||||||
},
|
|
||||||
// 是否显示提示
|
|
||||||
isShowTip: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// 禁用组件(仅查看文件)
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const emit = defineEmits();
|
|
||||||
const number = ref(0);
|
|
||||||
const uploadList = ref([]);
|
|
||||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
|
||||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
|
|
||||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
|
||||||
const fileList = ref([]);
|
|
||||||
const showTip = computed(
|
|
||||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(() => props.modelValue, val => {
|
|
||||||
if (val) {
|
|
||||||
let temp = 1;
|
|
||||||
// 首先将值转为数组
|
|
||||||
const list = Array.isArray(val) ? val : props.modelValue.split(',');
|
|
||||||
// 然后将数组转为对象数组
|
|
||||||
fileList.value = list.map(item => {
|
|
||||||
if (typeof item === "string") {
|
|
||||||
item = { name: item, url: item };
|
|
||||||
}
|
|
||||||
item.uid = item.uid || new Date().getTime() + temp++;
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fileList.value = [];
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},{ deep: true, immediate: true });
|
|
||||||
|
|
||||||
// 上传前校检格式和大小
|
|
||||||
function handleBeforeUpload(file) {
|
|
||||||
// 校检文件类型
|
|
||||||
if (props.fileType.length) {
|
|
||||||
const fileName = file.name.split('.');
|
|
||||||
const fileExt = fileName[fileName.length - 1];
|
|
||||||
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
|
|
||||||
if (!isTypeOk) {
|
|
||||||
proxy.$modal.msgError(`文件格式不正确,请上传${props.fileType.join("/")}格式文件!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 校检文件名是否包含特殊字符
|
|
||||||
if (file.name.includes(',')) {
|
|
||||||
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 校检文件大小
|
|
||||||
if (props.fileSize) {
|
|
||||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
|
||||||
if (!isLt) {
|
|
||||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proxy.$modal.loading("正在上传文件,请稍候...");
|
|
||||||
number.value++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文件个数超出
|
|
||||||
function handleExceed() {
|
|
||||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传失败
|
|
||||||
function handleUploadError(err) {
|
|
||||||
proxy.$modal.msgError("上传文件失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传成功回调
|
|
||||||
function handleUploadSuccess(res, file) {
|
|
||||||
if (res.code === 200) {
|
|
||||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
|
||||||
uploadedSuccessfully();
|
|
||||||
} else {
|
|
||||||
number.value--;
|
|
||||||
proxy.$modal.closeLoading();
|
|
||||||
proxy.$modal.msgError(res.msg);
|
|
||||||
proxy.$refs.fileUpload.handleRemove(file);
|
|
||||||
uploadedSuccessfully();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除文件
|
|
||||||
function handleDelete(index) {
|
|
||||||
fileList.value.splice(index, 1);
|
|
||||||
emit("update:modelValue", listToString(fileList.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传结束处理
|
|
||||||
function uploadedSuccessfully() {
|
|
||||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
|
||||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
|
||||||
uploadList.value = [];
|
|
||||||
number.value = 0;
|
|
||||||
emit("update:modelValue", listToString(fileList.value));
|
|
||||||
proxy.$modal.closeLoading();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取文件名称
|
|
||||||
function getFileName(name) {
|
|
||||||
// 如果是url那么取最后的名字 如果不是直接返回
|
|
||||||
if (name.lastIndexOf("/") > -1) {
|
|
||||||
return name.slice(name.lastIndexOf("/") + 1);
|
|
||||||
} else {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对象转成指定字符串分隔
|
|
||||||
function listToString(list, separator) {
|
|
||||||
let strs = "";
|
|
||||||
separator = separator || ",";
|
|
||||||
for (let i in list) {
|
|
||||||
if (list[i].url) {
|
|
||||||
strs += list[i].url + separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.upload-file-uploader {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.upload-file-list .el-upload-list__item {
|
|
||||||
border: 1px solid #e4e7ed;
|
|
||||||
line-height: 2;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.upload-file-list .ele-upload-list__item-content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
.ele-upload-list__item-content-action .el-link {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
<template>
|
|
||||||
<div :class="{ 'show': show }" class="header-search">
|
|
||||||
<!-- <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" /> -->
|
|
||||||
<el-select
|
|
||||||
ref="headerSearchSelectRef"
|
|
||||||
v-model="search"
|
|
||||||
:remote-method="querySearch"
|
|
||||||
filterable
|
|
||||||
default-first-option
|
|
||||||
remote
|
|
||||||
placeholder="搜索"
|
|
||||||
class="header-search-select"
|
|
||||||
@change="change"
|
|
||||||
>
|
|
||||||
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import Fuse from 'fuse.js'
|
|
||||||
import { getNormalPath } from '@/utils/dkl'
|
|
||||||
import { isHttp } from '@/utils/validate'
|
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
|
||||||
|
|
||||||
const search = ref('');
|
|
||||||
const options = ref([]);
|
|
||||||
const searchPool = ref([]);
|
|
||||||
const show = ref(false);
|
|
||||||
const fuse = ref(undefined);
|
|
||||||
const headerSearchSelectRef = ref(null);
|
|
||||||
const router = useRouter();
|
|
||||||
const routes = computed(() => usePermissionStore().defaultRoutes);
|
|
||||||
|
|
||||||
function click() {
|
|
||||||
show.value = !show.value
|
|
||||||
if (show.value) {
|
|
||||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function close() {
|
|
||||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
|
||||||
options.value = []
|
|
||||||
show.value = false
|
|
||||||
}
|
|
||||||
function change(val) {
|
|
||||||
const path = val.path;
|
|
||||||
const query = val.query;
|
|
||||||
if (isHttp(path)) {
|
|
||||||
// http(s):// 路径新窗口打开
|
|
||||||
const pindex = path.indexOf("http");
|
|
||||||
window.open(path.substr(pindex, path.length), "_blank");
|
|
||||||
} else {
|
|
||||||
if (query) {
|
|
||||||
router.push({ path: path, query: JSON.parse(query) });
|
|
||||||
} else {
|
|
||||||
router.push(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
search.value = ''
|
|
||||||
options.value = []
|
|
||||||
nextTick(() => {
|
|
||||||
show.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function initFuse(list) {
|
|
||||||
fuse.value = new Fuse(list, {
|
|
||||||
shouldSort: true,
|
|
||||||
threshold: 0.4,
|
|
||||||
location: 0,
|
|
||||||
distance: 100,
|
|
||||||
minMatchCharLength: 1,
|
|
||||||
keys: [{
|
|
||||||
name: 'title',
|
|
||||||
weight: 0.7
|
|
||||||
}, {
|
|
||||||
name: 'path',
|
|
||||||
weight: 0.3
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Filter out the routes that can be displayed in the sidebar
|
|
||||||
// And generate the internationalized title
|
|
||||||
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
|
||||||
let res = []
|
|
||||||
|
|
||||||
for (const r of routes) {
|
|
||||||
// skip hidden router
|
|
||||||
if (r.hidden) { continue }
|
|
||||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
|
||||||
const data = {
|
|
||||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
|
||||||
title: [...prefixTitle]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r.meta && r.meta.title) {
|
|
||||||
data.title = [...data.title, r.meta.title]
|
|
||||||
|
|
||||||
if (r.redirect !== 'noRedirect') {
|
|
||||||
// only push the routes with title
|
|
||||||
// special case: need to exclude parent router without redirect
|
|
||||||
res.push(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (r.query) {
|
|
||||||
data.query = r.query
|
|
||||||
}
|
|
||||||
|
|
||||||
// recursive child routes
|
|
||||||
if (r.children) {
|
|
||||||
const tempRoutes = generateRoutes(r.children, data.path, data.title)
|
|
||||||
if (tempRoutes.length >= 1) {
|
|
||||||
res = [...res, ...tempRoutes]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
function querySearch(query) {
|
|
||||||
if (query !== '') {
|
|
||||||
options.value = fuse.value.search(query)
|
|
||||||
} else {
|
|
||||||
options.value = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
searchPool.value = generateRoutes(routes.value);
|
|
||||||
})
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
searchPool.value = generateRoutes(routes.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(show, (value) => {
|
|
||||||
if (value) {
|
|
||||||
document.body.addEventListener('click', close)
|
|
||||||
} else {
|
|
||||||
document.body.removeEventListener('click', close)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(searchPool, (list) => {
|
|
||||||
initFuse(list)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.header-search {
|
|
||||||
font-size: 0 !important;
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center;
|
|
||||||
.search-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 18px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-search-select {
|
|
||||||
font-size: 18px;
|
|
||||||
transition: width 0.2s;
|
|
||||||
width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background: transparent;
|
|
||||||
border-radius: 0;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
:deep(.el-input__inner) {
|
|
||||||
border-radius: 0;
|
|
||||||
border: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
box-shadow: none !important;
|
|
||||||
border-bottom: 1px solid #d9d9d9;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.show {
|
|
||||||
.header-search-select {
|
|
||||||
width: 210px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="icon-body">
|
|
||||||
<el-input
|
|
||||||
v-model="iconName"
|
|
||||||
class="icon-search"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入图标名称"
|
|
||||||
@clear="filterIcons"
|
|
||||||
@input="filterIcons"
|
|
||||||
>
|
|
||||||
<template #suffix><i class="el-icon-search el-input__icon" /></template>
|
|
||||||
</el-input>
|
|
||||||
<div class="icon-list">
|
|
||||||
<div class="list-container">
|
|
||||||
<div v-for="(item, index) in iconList" class="icon-item-wrapper" :key="index" @click="selectedIcon(item)">
|
|
||||||
<div :class="['icon-item', { active: activeIcon === item }]">
|
|
||||||
<svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/>
|
|
||||||
<span>{{ item }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import icons from './requireIcons'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
activeIcon: {
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const iconName = ref('');
|
|
||||||
const iconList = ref(icons);
|
|
||||||
const emit = defineEmits(['selected']);
|
|
||||||
|
|
||||||
function filterIcons() {
|
|
||||||
iconList.value = icons
|
|
||||||
if (iconName.value) {
|
|
||||||
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectedIcon(name) {
|
|
||||||
emit('selected', name)
|
|
||||||
document.body.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
iconName.value = ''
|
|
||||||
iconList.value = icons
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
reset
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.icon-body {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
.icon-search {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.icon-list {
|
|
||||||
height: 200px;
|
|
||||||
overflow: auto;
|
|
||||||
.list-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
.icon-item-wrapper {
|
|
||||||
width: calc(100% / 3);
|
|
||||||
height: 25px;
|
|
||||||
line-height: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
.icon-item {
|
|
||||||
display: flex;
|
|
||||||
max-width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 5px;
|
|
||||||
&:hover {
|
|
||||||
background: #ececec;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: -0.15em;
|
|
||||||
fill: currentColor;
|
|
||||||
padding-left: 2px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.icon-item.active {
|
|
||||||
background: #ececec;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
let icons = []
|
|
||||||
const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
|
|
||||||
for (const path in modules) {
|
|
||||||
const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
|
|
||||||
icons.push(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default icons
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-image
|
|
||||||
:src="`${realSrc}`"
|
|
||||||
fit="cover"
|
|
||||||
:style="`width:${realWidth};height:${realHeight};`"
|
|
||||||
:preview-src-list="realSrcList"
|
|
||||||
preview-teleported
|
|
||||||
>
|
|
||||||
<template #error>
|
|
||||||
<div class="image-slot">
|
|
||||||
<el-icon><picture-filled /></el-icon>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-image>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { isExternal } from "@/utils/validate";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
src: {
|
|
||||||
type: String,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: ""
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const realSrc = computed(() => {
|
|
||||||
if (!props.src) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let real_src = props.src.split(",")[0];
|
|
||||||
if (isExternal(real_src)) {
|
|
||||||
return real_src;
|
|
||||||
}
|
|
||||||
return import.meta.env.VITE_APP_BASE_API + real_src;
|
|
||||||
});
|
|
||||||
|
|
||||||
const realSrcList = computed(() => {
|
|
||||||
if (!props.src) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let real_src_list = props.src.split(",");
|
|
||||||
let srcList = [];
|
|
||||||
real_src_list.forEach(item => {
|
|
||||||
if (isExternal(item)) {
|
|
||||||
return srcList.push(item);
|
|
||||||
}
|
|
||||||
return srcList.push(import.meta.env.VITE_APP_BASE_API + item);
|
|
||||||
});
|
|
||||||
return srcList;
|
|
||||||
});
|
|
||||||
|
|
||||||
const realWidth = computed(() =>
|
|
||||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
|
||||||
);
|
|
||||||
|
|
||||||
const realHeight = computed(() =>
|
|
||||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.el-image {
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #ebeef5;
|
|
||||||
box-shadow: 0 0 5px 1px #ccc;
|
|
||||||
:deep(.el-image__inner) {
|
|
||||||
transition: all 0.3s;
|
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:deep(.image-slot) {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: #909399;
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="component-upload-image">
|
|
||||||
<el-upload
|
|
||||||
multiple
|
|
||||||
:action="uploadImgUrl"
|
|
||||||
list-type="picture-card"
|
|
||||||
:on-success="handleUploadSuccess"
|
|
||||||
:before-upload="handleBeforeUpload"
|
|
||||||
:limit="limit"
|
|
||||||
:on-error="handleUploadError"
|
|
||||||
:on-exceed="handleExceed"
|
|
||||||
ref="imageUpload"
|
|
||||||
:before-remove="handleDelete"
|
|
||||||
:show-file-list="true"
|
|
||||||
:headers="headers"
|
|
||||||
:file-list="fileList"
|
|
||||||
:on-preview="handlePictureCardPreview"
|
|
||||||
:class="{ hide: fileList.length >= limit }"
|
|
||||||
>
|
|
||||||
<el-icon class="avatar-uploader-icon"><plus /></el-icon>
|
|
||||||
</el-upload>
|
|
||||||
<!-- 上传提示 -->
|
|
||||||
<div class="el-upload__tip" v-if="showTip">
|
|
||||||
请上传
|
|
||||||
<template v-if="fileSize">
|
|
||||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
|
||||||
</template>
|
|
||||||
<template v-if="fileType">
|
|
||||||
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
|
|
||||||
</template>
|
|
||||||
的文件
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
v-model="dialogVisible"
|
|
||||||
title="预览"
|
|
||||||
width="800px"
|
|
||||||
append-to-body
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="dialogImageUrl"
|
|
||||||
style="display: block; max-width: 100%; margin: 0 auto"
|
|
||||||
/>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { getToken } from "@/utils/auth";
|
|
||||||
import { isExternal } from "@/utils/validate";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: [String, Object, Array],
|
|
||||||
// 图片数量限制
|
|
||||||
limit: {
|
|
||||||
type: Number,
|
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
// 大小限制(MB)
|
|
||||||
fileSize: {
|
|
||||||
type: Number,
|
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
|
||||||
fileType: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ["png", "jpg", "jpeg"],
|
|
||||||
},
|
|
||||||
// 是否显示提示
|
|
||||||
isShowTip: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const emit = defineEmits();
|
|
||||||
const number = ref(0);
|
|
||||||
const uploadList = ref([]);
|
|
||||||
const dialogImageUrl = ref("");
|
|
||||||
const dialogVisible = ref(false);
|
|
||||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
|
||||||
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
|
||||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
|
||||||
const fileList = ref([]);
|
|
||||||
const showTip = computed(
|
|
||||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(() => props.modelValue, val => {
|
|
||||||
if (val) {
|
|
||||||
// 首先将值转为数组
|
|
||||||
const list = Array.isArray(val) ? val : props.modelValue.split(",");
|
|
||||||
// 然后将数组转为对象数组
|
|
||||||
fileList.value = list.map(item => {
|
|
||||||
if (typeof item === "string") {
|
|
||||||
if (item.indexOf(baseUrl) === -1 && !isExternal(item)) {
|
|
||||||
item = { name: baseUrl + item, url: baseUrl + item };
|
|
||||||
} else {
|
|
||||||
item = { name: item, url: item };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
fileList.value = [];
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},{ deep: true, immediate: true });
|
|
||||||
|
|
||||||
// 上传前loading加载
|
|
||||||
function handleBeforeUpload(file) {
|
|
||||||
let isImg = false;
|
|
||||||
if (props.fileType.length) {
|
|
||||||
let fileExtension = "";
|
|
||||||
if (file.name.lastIndexOf(".") > -1) {
|
|
||||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
|
||||||
}
|
|
||||||
isImg = props.fileType.some(type => {
|
|
||||||
if (file.type.indexOf(type) > -1) return true;
|
|
||||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
isImg = file.type.indexOf("image") > -1;
|
|
||||||
}
|
|
||||||
if (!isImg) {
|
|
||||||
proxy.$modal.msgError(`文件格式不正确,请上传${props.fileType.join("/")}图片格式文件!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (file.name.includes(',')) {
|
|
||||||
proxy.$modal.msgError('文件名不正确,不能包含英文逗号!');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (props.fileSize) {
|
|
||||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
|
||||||
if (!isLt) {
|
|
||||||
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
proxy.$modal.loading("正在上传图片,请稍候...");
|
|
||||||
number.value++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文件个数超出
|
|
||||||
function handleExceed() {
|
|
||||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传成功回调
|
|
||||||
function handleUploadSuccess(res, file) {
|
|
||||||
if (res.code === 200) {
|
|
||||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
|
||||||
uploadedSuccessfully();
|
|
||||||
} else {
|
|
||||||
number.value--;
|
|
||||||
proxy.$modal.closeLoading();
|
|
||||||
proxy.$modal.msgError(res.msg);
|
|
||||||
proxy.$refs.imageUpload.handleRemove(file);
|
|
||||||
uploadedSuccessfully();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除图片
|
|
||||||
function handleDelete(file) {
|
|
||||||
const findex = fileList.value.map(f => f.name).indexOf(file.name);
|
|
||||||
if (findex > -1 && uploadList.value.length === number.value) {
|
|
||||||
fileList.value.splice(findex, 1);
|
|
||||||
emit("update:modelValue", listToString(fileList.value));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传结束处理
|
|
||||||
function uploadedSuccessfully() {
|
|
||||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
|
||||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
|
||||||
uploadList.value = [];
|
|
||||||
number.value = 0;
|
|
||||||
emit("update:modelValue", listToString(fileList.value));
|
|
||||||
proxy.$modal.closeLoading();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传失败
|
|
||||||
function handleUploadError() {
|
|
||||||
proxy.$modal.msgError("上传图片失败");
|
|
||||||
proxy.$modal.closeLoading();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 预览
|
|
||||||
function handlePictureCardPreview(file) {
|
|
||||||
dialogImageUrl.value = file.url;
|
|
||||||
dialogVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对象转成指定字符串分隔
|
|
||||||
function listToString(list, separator) {
|
|
||||||
let strs = "";
|
|
||||||
separator = separator || ",";
|
|
||||||
for (let i in list) {
|
|
||||||
if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
|
|
||||||
strs += list[i].url.replace(baseUrl, "") + separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strs != "" ? strs.substr(0, strs.length - 1) : "";
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
// .el-upload--picture-card 控制加号部分
|
|
||||||
:deep(.hide .el-upload--picture-card) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
<template>
|
|
||||||
<div :class="{ 'hidden': hidden }" class="pagination-container">
|
|
||||||
<el-pagination
|
|
||||||
:background="background"
|
|
||||||
v-model:current-page="currentPage"
|
|
||||||
v-model:page-size="pageSize"
|
|
||||||
:layout="layout"
|
|
||||||
:page-sizes="pageSizes"
|
|
||||||
:pager-count="pagerCount"
|
|
||||||
:total="total"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { scrollTo } from '@/utils/scroll-to'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
total: {
|
|
||||||
required: true,
|
|
||||||
type: Number
|
|
||||||
},
|
|
||||||
page: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
limit: {
|
|
||||||
type: Number,
|
|
||||||
default: 20
|
|
||||||
},
|
|
||||||
pageSizes: {
|
|
||||||
type: Array,
|
|
||||||
default() {
|
|
||||||
return [10, 20, 30, 50]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 移动端页码按钮的数量端默认值5
|
|
||||||
pagerCount: {
|
|
||||||
type: Number,
|
|
||||||
default: document.body.clientWidth < 992 ? 5 : 7
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
type: String,
|
|
||||||
default: 'total, sizes, prev, pager, next, jumper'
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
autoScroll: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
hidden: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits();
|
|
||||||
const currentPage = computed({
|
|
||||||
get() {
|
|
||||||
return props.page
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
emit('update:page', val)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const pageSize = computed({
|
|
||||||
get() {
|
|
||||||
return props.limit
|
|
||||||
},
|
|
||||||
set(val){
|
|
||||||
emit('update:limit', val)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
function handleSizeChange(val) {
|
|
||||||
if (currentPage.value * val > props.total) {
|
|
||||||
currentPage.value = 1
|
|
||||||
}
|
|
||||||
emit('pagination', { page: currentPage.value, limit: val })
|
|
||||||
if (props.autoScroll) {
|
|
||||||
scrollTo(0, 800)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function handleCurrentChange(val) {
|
|
||||||
emit('pagination', { page: val, limit: pageSize.value })
|
|
||||||
if (props.autoScroll) {
|
|
||||||
scrollTo(0, 800)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.pagination-container {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.pagination-container.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<template >
|
|
||||||
<router-view />
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<template>
|
|
||||||
<div ref="editorRef"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, watch } from 'vue';
|
|
||||||
import Quill from 'quill';
|
|
||||||
import 'quill/dist/quill.snow.css';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
content: String,
|
|
||||||
options: Object
|
|
||||||
});
|
|
||||||
const emit = defineEmits(['update:content']);
|
|
||||||
|
|
||||||
const editorRef = ref();
|
|
||||||
let quill = null;
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
quill = new Quill(editorRef.value, {
|
|
||||||
theme: 'snow',
|
|
||||||
modules: props.options?.modules || {
|
|
||||||
toolbar: [
|
|
||||||
['bold', 'italic'],
|
|
||||||
[{ header: [1, 2, false] }],
|
|
||||||
['link', 'image']
|
|
||||||
]
|
|
||||||
},
|
|
||||||
placeholder: props.options?.placeholder || '请输入内容...'
|
|
||||||
});
|
|
||||||
|
|
||||||
quill.on('text-change', () => {
|
|
||||||
emit('update:content', quill.root.innerHTML);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (props.content) {
|
|
||||||
quill.root.innerHTML = props.content;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => props.content, (newVal) => {
|
|
||||||
if (quill && newVal !== quill.root.innerHTML) {
|
|
||||||
quill.root.innerHTML = newVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,134 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="top-right-btn" :style="style">
|
|
||||||
<el-row>
|
|
||||||
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
|
|
||||||
<el-button circle icon="Search" @click="toggleSearch()" />
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
|
||||||
<el-button circle icon="Refresh" @click="refresh()" />
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
|
|
||||||
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
|
||||||
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
|
||||||
<el-button circle icon="Menu" />
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<template v-for="item in columns" :key="item.key">
|
|
||||||
<el-dropdown-item>
|
|
||||||
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
|
|
||||||
</el-dropdown-item>
|
|
||||||
</template>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
</el-tooltip>
|
|
||||||
</el-row>
|
|
||||||
<el-dialog :title="title" v-model="open" append-to-body>
|
|
||||||
<el-transfer
|
|
||||||
:titles="['显示', '隐藏']"
|
|
||||||
v-model="value"
|
|
||||||
:data="columns"
|
|
||||||
@change="dataChange"
|
|
||||||
></el-transfer>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
/* 是否显示检索条件 */
|
|
||||||
showSearch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
/* 显隐列信息 */
|
|
||||||
columns: {
|
|
||||||
type: Array,
|
|
||||||
},
|
|
||||||
/* 是否显示检索图标 */
|
|
||||||
search: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
|
||||||
showColumnsType: {
|
|
||||||
type: String,
|
|
||||||
default: "checkbox",
|
|
||||||
},
|
|
||||||
/* 右外边距 */
|
|
||||||
gutter: {
|
|
||||||
type: Number,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emits = defineEmits(['update:showSearch', 'queryTable']);
|
|
||||||
|
|
||||||
// 显隐数据
|
|
||||||
const value = ref([]);
|
|
||||||
// 弹出层标题
|
|
||||||
const title = ref("显示/隐藏");
|
|
||||||
// 是否显示弹出层
|
|
||||||
const open = ref(false);
|
|
||||||
|
|
||||||
const style = computed(() => {
|
|
||||||
const ret = {};
|
|
||||||
if (props.gutter) {
|
|
||||||
ret.marginRight = `${props.gutter / 2}px`;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 搜索
|
|
||||||
function toggleSearch() {
|
|
||||||
emits("update:showSearch", !props.showSearch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新
|
|
||||||
function refresh() {
|
|
||||||
emits("queryTable");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 右侧列表元素变化
|
|
||||||
function dataChange(data) {
|
|
||||||
for (let item in props.columns) {
|
|
||||||
const key = props.columns[item].key;
|
|
||||||
props.columns[item].visible = !data.includes(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开显隐列dialog
|
|
||||||
function showColumn() {
|
|
||||||
open.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.showColumnsType == 'transfer') {
|
|
||||||
// 显隐列初始默认隐藏列
|
|
||||||
for (let item in props.columns) {
|
|
||||||
if (props.columns[item].visible === false) {
|
|
||||||
value.value.push(parseInt(item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 勾选
|
|
||||||
function checkboxChange(event, label) {
|
|
||||||
props.columns.filter(item => item.label == label)[0].visible = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
:deep(.el-transfer__button) {
|
|
||||||
border-radius: 50%;
|
|
||||||
display: block;
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
:deep(.el-transfer__button:first-child) {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
:deep(.el-dropdown-menu__item) {
|
|
||||||
line-height: 30px;
|
|
||||||
padding: 0 17px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- <svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" /> -->
|
|
||||||
<img src="../../assets/images/qp.png" @click="toggle" alt="" style="width:32px;height:32px;">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useFullscreen } from '@vueuse/core'
|
|
||||||
|
|
||||||
const { isFullscreen, enter, exit, toggle } = useFullscreen();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.screenfull-svg {
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
fill: #5a5e66;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
vertical-align: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-dropdown trigger="click" @command="handleSetSize">
|
|
||||||
<div class="size-icon--style">
|
|
||||||
<svg-icon class-name="size-icon" icon-class="size" />
|
|
||||||
</div>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size === item.value" :command="item.value">
|
|
||||||
{{ item.label }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import useAppStore from "@/store/modules/app";
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const size = computed(() => appStore.size);
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const sizeOptions = ref([
|
|
||||||
{ label: "较大", value: "large" },
|
|
||||||
{ label: "默认", value: "default" },
|
|
||||||
{ label: "稍小", value: "small" },
|
|
||||||
]);
|
|
||||||
|
|
||||||
function handleSetSize(size) {
|
|
||||||
proxy.$modal.loading("正在设置布局大小,请稍候...");
|
|
||||||
appStore.setSize(size);
|
|
||||||
setTimeout("window.location.reload()", 1000);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.size-icon--style {
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 50px;
|
|
||||||
padding-right: 7px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<template>
|
|
||||||
<svg :class="svgClass" aria-hidden="true">
|
|
||||||
<use :xlink:href="iconName" :fill="color" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default defineComponent({
|
|
||||||
props: {
|
|
||||||
iconClass: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
return {
|
|
||||||
iconName: computed(() => `#icon-${props.iconClass}`),
|
|
||||||
svgClass: computed(() => {
|
|
||||||
if (props.className) {
|
|
||||||
return `svg-icon ${props.className}`
|
|
||||||
}
|
|
||||||
return 'svg-icon'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scope lang="scss">
|
|
||||||
.sub-el-icon,
|
|
||||||
.nav-icon {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 15px;
|
|
||||||
margin-right: 12px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.svg-icon {
|
|
||||||
width: 1em;
|
|
||||||
height: 1em;
|
|
||||||
position: relative;
|
|
||||||
fill: currentColor;
|
|
||||||
vertical-align: -2px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import * as components from '@element-plus/icons-vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
install: (app) => {
|
|
||||||
for (const key in components) {
|
|
||||||
const componentConfig = components[key];
|
|
||||||
app.component(componentConfig.name, componentConfig);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-menu
|
|
||||||
:default-active="activeMenu"
|
|
||||||
mode="horizontal"
|
|
||||||
@select="handleSelect"
|
|
||||||
:ellipsis="false"
|
|
||||||
>
|
|
||||||
<template v-for="(item, index) in topMenus">
|
|
||||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
|
|
||||||
<svg-icon
|
|
||||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
|
||||||
:icon-class="item.meta.icon"/>
|
|
||||||
{{ item.meta.title }}
|
|
||||||
</el-menu-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 顶部菜单超出数量折叠 -->
|
|
||||||
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
|
||||||
<template #title>更多菜单</template>
|
|
||||||
<template v-for="(item, index) in topMenus">
|
|
||||||
<el-menu-item
|
|
||||||
:index="item.path"
|
|
||||||
:key="index"
|
|
||||||
v-if="index >= visibleNumber">
|
|
||||||
<svg-icon
|
|
||||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
|
||||||
:icon-class="item.meta.icon"/>
|
|
||||||
{{ item.meta.title }}
|
|
||||||
</el-menu-item>
|
|
||||||
</template>
|
|
||||||
</el-sub-menu>
|
|
||||||
</el-menu>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { constantRoutes } from "@/router"
|
|
||||||
import { isHttp } from '@/utils/validate'
|
|
||||||
import useAppStore from '@/store/modules/app'
|
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
|
||||||
|
|
||||||
// 顶部栏初始数
|
|
||||||
const visibleNumber = ref(null);
|
|
||||||
// 当前激活菜单的 index
|
|
||||||
const currentIndex = ref(null);
|
|
||||||
// 隐藏侧边栏路由
|
|
||||||
const hideList = ['/index', '/user/profile'];
|
|
||||||
|
|
||||||
const appStore = useAppStore()
|
|
||||||
const settingsStore = useSettingsStore()
|
|
||||||
const permissionStore = usePermissionStore()
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
// 主题颜色
|
|
||||||
const theme = computed(() => settingsStore.theme);
|
|
||||||
// 所有的路由信息
|
|
||||||
const routers = computed(() => permissionStore.topbarRouters);
|
|
||||||
|
|
||||||
// 顶部显示菜单
|
|
||||||
const topMenus = computed(() => {
|
|
||||||
let topMenus = [];
|
|
||||||
routers.value.map((menu) => {
|
|
||||||
if (menu.hidden !== true) {
|
|
||||||
// 兼容顶部栏一级菜单内部跳转
|
|
||||||
if (menu.path === "/") {
|
|
||||||
topMenus.push(menu.children[0]);
|
|
||||||
} else {
|
|
||||||
topMenus.push(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return topMenus;
|
|
||||||
})
|
|
||||||
|
|
||||||
// 设置子路由
|
|
||||||
const childrenMenus = computed(() => {
|
|
||||||
let childrenMenus = [];
|
|
||||||
routers.value.map((router) => {
|
|
||||||
for (let item in router.children) {
|
|
||||||
if (router.children[item].parentPath === undefined) {
|
|
||||||
if(router.path === "/") {
|
|
||||||
router.children[item].path = "/" + router.children[item].path;
|
|
||||||
} else {
|
|
||||||
if(!isHttp(router.children[item].path)) {
|
|
||||||
router.children[item].path = router.path + "/" + router.children[item].path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
router.children[item].parentPath = router.path;
|
|
||||||
}
|
|
||||||
childrenMenus.push(router.children[item]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return constantRoutes.concat(childrenMenus);
|
|
||||||
})
|
|
||||||
|
|
||||||
// 默认激活的菜单
|
|
||||||
const activeMenu = computed(() => {
|
|
||||||
const path = route.path;
|
|
||||||
let activePath = path;
|
|
||||||
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
|
|
||||||
const tmpPath = path.substring(1, path.length);
|
|
||||||
if (!route.meta.link) {
|
|
||||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
|
|
||||||
appStore.toggleSideBarHide(false);
|
|
||||||
}
|
|
||||||
} else if(!route.children) {
|
|
||||||
activePath = path;
|
|
||||||
appStore.toggleSideBarHide(true);
|
|
||||||
}
|
|
||||||
activeRoutes(activePath);
|
|
||||||
return activePath;
|
|
||||||
})
|
|
||||||
|
|
||||||
function setVisibleNumber() {
|
|
||||||
const width = document.body.getBoundingClientRect().width / 3;
|
|
||||||
visibleNumber.value = parseInt(width / 85);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSelect(key, keyPath) {
|
|
||||||
currentIndex.value = key;
|
|
||||||
const route = routers.value.find(item => item.path === key);
|
|
||||||
if (isHttp(key)) {
|
|
||||||
// http(s):// 路径新窗口打开
|
|
||||||
window.open(key, "_blank");
|
|
||||||
} else if (!route || !route.children) {
|
|
||||||
// 没有子路由路径内部打开
|
|
||||||
const routeMenu = childrenMenus.value.find(item => item.path === key);
|
|
||||||
if (routeMenu && routeMenu.query) {
|
|
||||||
let query = JSON.parse(routeMenu.query);
|
|
||||||
router.push({ path: key, query: query });
|
|
||||||
} else {
|
|
||||||
router.push({ path: key });
|
|
||||||
}
|
|
||||||
appStore.toggleSideBarHide(true);
|
|
||||||
} else {
|
|
||||||
// 显示左侧联动菜单
|
|
||||||
activeRoutes(key);
|
|
||||||
appStore.toggleSideBarHide(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function activeRoutes(key) {
|
|
||||||
let routes = [];
|
|
||||||
if (childrenMenus.value && childrenMenus.value.length > 0) {
|
|
||||||
childrenMenus.value.map((item) => {
|
|
||||||
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
|
||||||
routes.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(routes.length > 0) {
|
|
||||||
permissionStore.setSidebarRouters(routes);
|
|
||||||
} else {
|
|
||||||
appStore.toggleSideBarHide(true);
|
|
||||||
}
|
|
||||||
return routes;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('resize', setVisibleNumber)
|
|
||||||
})
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('resize', setVisibleNumber)
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setVisibleNumber()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.topmenu-container.el-menu--horizontal > .el-menu-item {
|
|
||||||
float: left;
|
|
||||||
height: 50px !important;
|
|
||||||
line-height: 50px !important;
|
|
||||||
color: #999093 !important;
|
|
||||||
padding: 0 5px !important;
|
|
||||||
margin: 0 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
|
|
||||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sub-menu item */
|
|
||||||
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
|
|
||||||
float: left;
|
|
||||||
height: 50px !important;
|
|
||||||
line-height: 50px !important;
|
|
||||||
color: #999093 !important;
|
|
||||||
padding: 0 5px !important;
|
|
||||||
margin: 0 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 背景色隐藏 */
|
|
||||||
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图标右间距 */
|
|
||||||
.topmenu-container .svg-icon {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* topmenu more arrow */
|
|
||||||
.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
|
|
||||||
position: static;
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { onMounted, onBeforeUnmount, watch } from "vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
videoUrl: String
|
|
||||||
});
|
|
||||||
|
|
||||||
const playerContainer = ref(null);
|
|
||||||
const player = ref(null);
|
|
||||||
|
|
||||||
const initPlayer = () => {
|
|
||||||
if (!props.videoUrl || !playerContainer.value) return;
|
|
||||||
|
|
||||||
// 彻底清理旧实例(关键修改)
|
|
||||||
if (player.value) {
|
|
||||||
player.value.JS_StopRealPlayAll();
|
|
||||||
player.value.JS_Destroy();
|
|
||||||
playerContainer.value.innerHTML = ""; // 清空容器
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新创建容器(避免残留元素)
|
|
||||||
const container = document.createElement("div");
|
|
||||||
container.id = "player-container";
|
|
||||||
container.style.width = "100%";
|
|
||||||
container.style.height = "100%";
|
|
||||||
playerContainer.value.appendChild(container);
|
|
||||||
|
|
||||||
// 初始化新实例
|
|
||||||
player.value = new JSPlugin({
|
|
||||||
szId: "player-container", // 必须与创建的容器ID一致
|
|
||||||
szBasePath: "js/",
|
|
||||||
openDebug: true,
|
|
||||||
supportGMProtocol: true, // 启用国密协议支持
|
|
||||||
iWidth:"100%",
|
|
||||||
iHeight:"100%",
|
|
||||||
});
|
|
||||||
|
|
||||||
player.value.JS_Play(props.videoUrl, {
|
|
||||||
onError: (err) => console.error("播放失败:", err)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听URL变化
|
|
||||||
watch(() => props.videoUrl, initPlayer);
|
|
||||||
|
|
||||||
onMounted(initPlayer);
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
if (player.value) {
|
|
||||||
player.value.JS_StopRealPlayAll();
|
|
||||||
player.value.JS_Destroy();
|
|
||||||
}
|
|
||||||
if (playerContainer.value) {
|
|
||||||
playerContainer.value.innerHTML = ""; // 确保清理
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- 固定结构,不设ID -->
|
|
||||||
<div ref="playerContainer" class="video-monitor-container"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.video-monitor-container {
|
|
||||||
width: 95%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 500px; /* 防止折叠 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog
|
|
||||||
class="dialog"
|
|
||||||
v-model="dialogVisible"
|
|
||||||
:title="videoTitle"
|
|
||||||
:width="width"
|
|
||||||
:before-close="handleClose"
|
|
||||||
>
|
|
||||||
<div ref="domRef" class="video"></div>
|
|
||||||
<template #footer>
|
|
||||||
<span class="dialog-footer">
|
|
||||||
<el-button @click="handleClose">关 闭</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup name="VideoPlay">
|
|
||||||
import VideoPlayer from "xgplayer";
|
|
||||||
import "xgplayer/dist/index.min.css";
|
|
||||||
import {computed, nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
required: true,
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
videoTitle: {
|
|
||||||
type: String,
|
|
||||||
default: "视频播放",
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
required: true,
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: "600px",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const emit = defineEmits(["update:visible"]);
|
|
||||||
|
|
||||||
// dom ref
|
|
||||||
const domRef = ref();
|
|
||||||
const player = ref();
|
|
||||||
|
|
||||||
// computed
|
|
||||||
const dialogVisible = computed(() => props.visible);
|
|
||||||
|
|
||||||
// 渲染播放器
|
|
||||||
function renderXgPlayer() {
|
|
||||||
if (!domRef.value) return;
|
|
||||||
const url = props.url;
|
|
||||||
player.value = new VideoPlayer({
|
|
||||||
el: domRef.value,
|
|
||||||
url,
|
|
||||||
playbackRate: [0.5, 0.75, 1, 1.5, 2],
|
|
||||||
// 允许自动播放
|
|
||||||
autoplay: true,
|
|
||||||
muted: true,
|
|
||||||
autoplayMuted: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 销毁播放器
|
|
||||||
function destroyXgPlayer() {
|
|
||||||
player.value?.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭弹窗
|
|
||||||
function handleClose() {
|
|
||||||
emit("update:visible", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听显示与隐藏 控制播放器播放与销毁
|
|
||||||
watch(
|
|
||||||
() => dialogVisible.value,
|
|
||||||
(val) => {
|
|
||||||
if (val) {
|
|
||||||
nextTick(() => {
|
|
||||||
renderXgPlayer();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
destroyXgPlayer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.dialog {
|
|
||||||
box-sizing: border-box;
|
|
||||||
.video {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
<template>
|
|
||||||
<!-- <el-dialog
|
|
||||||
class="dialog"
|
|
||||||
v-model="dialogVisible"
|
|
||||||
:title="videoTitle"
|
|
||||||
:width="width"
|
|
||||||
:before-close="handleClose"
|
|
||||||
> -->
|
|
||||||
<div ref="domRef" class="video" v-if="dialogVisible"></div>
|
|
||||||
<!-- <template #footer>
|
|
||||||
<span class="dialog-footer">
|
|
||||||
<el-button @click="handleClose">关 闭</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-dialog> -->
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup name="VideoPlay">
|
|
||||||
import VideoPlayer from "xgplayer";
|
|
||||||
import "xgplayer/dist/index.min.css";
|
|
||||||
import {computed, nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
required: true,
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
videoTitle: {
|
|
||||||
type: String,
|
|
||||||
default: "视频播放",
|
|
||||||
},
|
|
||||||
url: {
|
|
||||||
required: true,
|
|
||||||
type: String,
|
|
||||||
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: "600px",
|
|
||||||
},
|
|
||||||
autoplay: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
loop: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const emit = defineEmits(["update:visible"]);
|
|
||||||
|
|
||||||
// dom ref
|
|
||||||
const domRef = ref();
|
|
||||||
const player = ref();
|
|
||||||
|
|
||||||
// computed
|
|
||||||
const dialogVisible = computed(() => props.visible);
|
|
||||||
|
|
||||||
// 渲染播放器
|
|
||||||
function renderXgPlayer() {
|
|
||||||
if (!domRef.value) return;
|
|
||||||
const url = props.url;
|
|
||||||
player.value = new VideoPlayer({
|
|
||||||
el: domRef.value,
|
|
||||||
url,
|
|
||||||
playbackRate: [0.5, 0.75, 1, 1.5, 2],
|
|
||||||
// 允许自动播放
|
|
||||||
loop:props.loop,
|
|
||||||
autoplay: props.autoplay,
|
|
||||||
muted: props.autoplay,
|
|
||||||
autoplayMuted: props.autoplay,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 销毁播放器
|
|
||||||
function destroyXgPlayer() {
|
|
||||||
player.value?.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭弹窗
|
|
||||||
function handleClose() {
|
|
||||||
emit("update:visible", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听显示与隐藏 控制播放器播放与销毁
|
|
||||||
watch(
|
|
||||||
() => dialogVisible.value,
|
|
||||||
(val) => {
|
|
||||||
if (val) {
|
|
||||||
nextTick(() => {
|
|
||||||
renderXgPlayer();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
destroyXgPlayer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
renderXgPlayer();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.video {
|
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
&:deep(.xgplayer-start) {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="wang-editor-container">
|
|
||||||
<Toolbar
|
|
||||||
class="editor-toolbar"
|
|
||||||
:editor="editorRef"
|
|
||||||
:defaultConfig="toolbarConfig"
|
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
<Editor
|
|
||||||
class="editor-content"
|
|
||||||
v-model="valueHtml"
|
|
||||||
:defaultConfig="editorConfig"
|
|
||||||
:style="{ height: `${height}px` }"
|
|
||||||
:mode="mode"
|
|
||||||
@onCreated="handleCreated"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, shallowRef, watch, onBeforeUnmount, onMounted } from 'vue'
|
|
||||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 400
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
|
|
||||||
const mode = ref('default') // 或 'simple'
|
|
||||||
const editorRef = shallowRef()
|
|
||||||
const valueHtml = ref(props.modelValue)
|
|
||||||
|
|
||||||
// 完整的工具栏配置
|
|
||||||
const toolbarConfig = {
|
|
||||||
excludeKeys: [
|
|
||||||
'group-image', // 排除图片
|
|
||||||
'group-video', // 排除视频
|
|
||||||
'emotion', // 排除表情
|
|
||||||
'insertImage',
|
|
||||||
'uploadImage'
|
|
||||||
],
|
|
||||||
insertKeys: {
|
|
||||||
index: 25, // 插入位置
|
|
||||||
keys: [
|
|
||||||
'insertTable', // 确保表格按钮存在
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑器配置
|
|
||||||
const editorConfig = ref({
|
|
||||||
placeholder: '请输入内容...',
|
|
||||||
scroll: true,
|
|
||||||
MENU_CONF: {
|
|
||||||
insertTable: {
|
|
||||||
maxRow: 10,
|
|
||||||
maxCol: 6,
|
|
||||||
// 自定义表格样式
|
|
||||||
tableCellStyle: {
|
|
||||||
border: '1px solid #ddd',
|
|
||||||
padding: '8px',
|
|
||||||
minWidth: '50px'
|
|
||||||
},
|
|
||||||
tableHeaderCellStyle: {
|
|
||||||
backgroundColor: '#f2f2f2',
|
|
||||||
border: '1px solid #ddd',
|
|
||||||
padding: '8px'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hoverbarKeys: {
|
|
||||||
table: {
|
|
||||||
menuKeys: [
|
|
||||||
'tableHeader',
|
|
||||||
'insertTableRow',
|
|
||||||
'deleteTableRow',
|
|
||||||
'insertTableCol',
|
|
||||||
'deleteTableCol',
|
|
||||||
'deleteTable'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleCreated = (editor) => {
|
|
||||||
editorRef.value = editor
|
|
||||||
// 强制刷新工具栏
|
|
||||||
nextTick(() => {
|
|
||||||
editor.getToolbar().updateAllToolbar()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保在组件挂载后初始化
|
|
||||||
onMounted(() => {
|
|
||||||
if (editorRef.value) {
|
|
||||||
editorRef.value.getToolbar().updateAllToolbar()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(valueHtml, (newVal) => {
|
|
||||||
emit('update:modelValue', newVal)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.modelValue, (newVal) => {
|
|
||||||
if (newVal !== valueHtml.value) {
|
|
||||||
valueHtml.value = newVal
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
if (editorRef.value) {
|
|
||||||
editorRef.value.destroy()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
getEditor: () => editorRef.value
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 必须引入的样式 */
|
|
||||||
@import '@wangeditor/editor/dist/css/style.css';
|
|
||||||
|
|
||||||
.wang-editor-container {
|
|
||||||
border: 1px solid #dcdfe6;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-toolbar {
|
|
||||||
border-bottom: 1px solid #dcdfe6 !important;
|
|
||||||
background-color: #f8f8f8 !important;
|
|
||||||
padding: 0 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-content {
|
|
||||||
padding: 10px 15px !important;
|
|
||||||
background-color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表格样式增强 */
|
|
||||||
.w-e-text table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-e-text table td,
|
|
||||||
.w-e-text table th {
|
|
||||||
border: 1px solid #ddd !important;
|
|
||||||
padding: 8px !important;
|
|
||||||
min-width: 50px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-e-text table th {
|
|
||||||
background-color: #f2f2f2 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 暗黑模式适配 */
|
|
||||||
[data-theme='dark'] .w-e-text table th {
|
|
||||||
background-color: #333 !important;
|
|
||||||
border-color: #444 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保工具栏按钮可点击 */
|
|
||||||
.w-e-bar .w-e-menu {
|
|
||||||
cursor: pointer !important;
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移除不需要的按钮 */
|
|
||||||
.w-e-bar [data-menu-key="uploadImage"],
|
|
||||||
.w-e-bar [data-menu-key="insertImage"],
|
|
||||||
.w-e-bar [data-menu-key="uploadVideo"],
|
|
||||||
.w-e-bar [data-menu-key="insertVideo"],
|
|
||||||
.w-e-bar [data-menu-key="emotion"] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
<template>
|
|
||||||
<div id="demo">
|
|
||||||
<iframe
|
|
||||||
src="https://10.22.245.209:18889/ktCallingSystem/wholeCallPage?agentId=1029&agentPwd=cinAgt123%23"
|
|
||||||
id="iframe"
|
|
||||||
style="width: 108plx; height: 108px; right: 20px; bottom: 20px"
|
|
||||||
allow="microphone;camera;midi;encrypted-media"
|
|
||||||
>
|
|
||||||
</iframe>
|
|
||||||
<el-button @click="call">拨打</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script setup name="call">
|
|
||||||
const props = defineProps({
|
|
||||||
listRows: {
|
|
||||||
required: true,
|
|
||||||
type: Object,
|
|
||||||
defult: {},
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
defult: "",
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
defult: "",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/**
|
|
||||||
* 监听iframe的消息
|
|
||||||
*/
|
|
||||||
window.addEventListener("message", function (msg) {
|
|
||||||
// 接受从iframe传来的消息
|
|
||||||
var data = msg.data;
|
|
||||||
if (data && typeof data === "string" && data.startsWith("{")) {
|
|
||||||
var dataObj = JSON.parse(data);
|
|
||||||
handleMessage(dataObj);
|
|
||||||
} else {
|
|
||||||
console.log("not a valid message");
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息处理
|
|
||||||
*/
|
|
||||||
function handleMessage(obj) {
|
|
||||||
console.log("receive message from iframe: ", obj);
|
|
||||||
var op = obj.op || "";
|
|
||||||
var value = obj.value || "";
|
|
||||||
const { width, height } = props;
|
|
||||||
switch (op) {
|
|
||||||
case "big_dialog": // 拨号、通话时窗口变大
|
|
||||||
var iframe = document.getElementById("iframe");
|
|
||||||
if(width || height) {
|
|
||||||
iframe.style.width = width;
|
|
||||||
iframe.style.height = height;
|
|
||||||
} else {
|
|
||||||
iframe.style.width = "1920px";
|
|
||||||
iframe.style.height = "1080px";
|
|
||||||
iframe.style.right = "0px";
|
|
||||||
iframe.style.bottom = "0px";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "small_dialog": // 返回电话盘
|
|
||||||
var iframe = document.getElementById("iframe");
|
|
||||||
iframe.style.width = "400px";
|
|
||||||
iframe.style.height = "300px";
|
|
||||||
iframe.style.right = "20px";
|
|
||||||
iframe.style.bottom = "20px";
|
|
||||||
break;
|
|
||||||
case "call_in": // 电话呼入
|
|
||||||
var iframe = document.getElementById("iframe");
|
|
||||||
iframe.style.width = "238px";
|
|
||||||
iframe.style.height = "108px";
|
|
||||||
case "move": // 电话盘拖动
|
|
||||||
var iframe = document.getElementById("iframe");
|
|
||||||
var offsetX = value.offsetX;
|
|
||||||
var offsetY = value.offsetY;
|
|
||||||
iframe.style.right = addPixel(iframe.style.right, -offsetX);
|
|
||||||
iframe.style.bottom = addPixel(iframe.style.bottom, -offsetY);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function addPixel(pixelStr, offset) {
|
|
||||||
var num = Number(pixelStr.replace("px", ""));
|
|
||||||
return num + offset + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
function call() {
|
|
||||||
const value = props.listRows.phone;
|
|
||||||
var iframeWindow = window.frames[0];
|
|
||||||
const msgObj = {
|
|
||||||
op: "call", // 拨号
|
|
||||||
value: value,
|
|
||||||
};
|
|
||||||
iframeWindow.postMessage(JSON.stringify(msgObj), "*");
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {});
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<svg-icon icon-class="question" @click="goto" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
// const url = ref('http://doc.dkl.vip/dkl-vue');
|
|
||||||
|
|
||||||
function goto() {
|
|
||||||
window.open(url.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<svg-icon icon-class="github" @click="goto" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
// const url = ref('https://gitee.com/y_project/dkl-Vue');
|
|
||||||
|
|
||||||
function goto() {
|
|
||||||
window.open(url.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<template>
|
|
||||||
<div v-loading="loading" :style="'height:' + height">
|
|
||||||
<iframe
|
|
||||||
:src="url"
|
|
||||||
frameborder="no"
|
|
||||||
style="width: 100%; height: 100%"
|
|
||||||
scrolling="auto" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
src: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const height = ref(document.documentElement.clientHeight - 94.5 + "px;")
|
|
||||||
const loading = ref(true)
|
|
||||||
const url = computed(() => props.src)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
loading.value = false;
|
|
||||||
}, 300);
|
|
||||||
window.onresize = function temp() {
|
|
||||||
height.value = document.documentElement.clientHeight - 94.5 + "px;";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue