Merge pull request #3 from TangSengDaoDao/dev

合并
This commit is contained in:
budou 2024-02-25 10:46:24 +08:00 committed by GitHub
commit 42094a1e16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 2739 additions and 62 deletions

View File

@ -13,6 +13,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@fancyapps/ui": "^5.0.22",
"@icon-park/vue-next": "^1.4.2",
"@lottiefiles/lottie-player": "^2.0.2",
"@vueuse/core": "^10.1.2",

26
pnpm-lock.yaml generated
View File

@ -8,6 +8,9 @@ dependencies:
'@element-plus/icons-vue':
specifier: ^2.1.0
version: 2.1.0(vue@3.3.4)
'@fancyapps/ui':
specifier: ^5.0.22
version: 5.0.22
'@icon-park/vue-next':
specifier: ^1.4.2
version: 1.4.2(vue@3.3.4)
@ -1033,6 +1036,10 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@fancyapps/ui@5.0.22:
resolution: {integrity: sha512-44A5/Hm59mn53FqZRyM4HO/R7UWE79UxgNyHmaudSe+HuU5Kl1g6lxR4Ol1QFaqNS97Pbdytlu4SxYsOX1BTxw==}
dev: false
/@floating-ui/core@1.3.1:
resolution: {integrity: sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==}
dev: false
@ -3988,7 +3995,7 @@ packages:
lodash-es: 4.17.21
vanilla-jsoneditor: 0.17.8
vue: 3.3.4
vue-demi: 0.14.5(vue@3.3.4)
vue-demi: 0.14.6(vue@3.3.4)
vue-global-config: 0.4.0(vue@3.3.4)
dev: true
@ -5750,6 +5757,21 @@ packages:
dependencies:
vue: 3.3.4
/vue-demi@0.14.6(vue@3.3.4):
resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
peerDependencies:
'@vue/composition-api': ^1.0.0-rc.1
vue: ^3.0.0-0 || ^2.6.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
dependencies:
vue: 3.3.4
dev: true
/vue-eslint-parser@9.3.1(eslint@8.40.0):
resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
engines: {node: ^14.17.0 || >=16.0.0}
@ -5772,7 +5794,7 @@ packages:
resolution: {integrity: sha512-283vpYOhVHJCpMkjNVEwZdgaAb+Y93zFaXGAWTI378MLoNuwQydjD/BAy1e81QYEmyA+JbxqcmMZVWcM9rbriw==}
dependencies:
eslint-config-vue-global-api: 0.4.1
vue-demi: 0.14.5(vue@3.3.4)
vue-demi: 0.14.6(vue@3.3.4)
transitivePeerDependencies:
- '@vue/composition-api'
- vue

36
src/api/command.ts Normal file
View File

@ -0,0 +1,36 @@
import request from '@/utils/axios';
// 获取口令列表
export function commandGet(params?: any) {
return request({
url: '/manager/web3/laboratory',
method: 'get',
params
});
}
// 新增口令
export function commandPost(data: any) {
return request({
url: '/manager/web3/laboratory',
method: 'post',
data
});
}
// 编辑口令
export function commandPut(data: any, id: string) {
return request({
url: `/manager/web3/laboratory/${id}`,
method: 'put',
data
});
}
// 删除口令
export function commandDelete(id: string) {
return request({
url: `/manager/web3/laboratory/${id}`,
method: 'delete'
});
}

36
src/api/workplace/app.ts Normal file
View File

@ -0,0 +1,36 @@
import request from '@/utils/axios';
// 获取应用
export function appGet(params?: any) {
return request({
url: '/manager/workplace/app',
method: 'get',
params
});
}
// 新增应用
export function appPost(data: any) {
return request({
url: '/manager/workplace/app',
method: 'post',
data
});
}
// 编辑应用
export function appPut(data: any, app_id: string) {
return request({
url: `/manager/workplace/apps/${app_id}`,
method: 'put',
data
});
}
// 删除应用
export function appDelete(app_id: string) {
return request({
url: `/manager/workplace/apps/${app_id}`,
method: 'delete'
});
}

View File

@ -0,0 +1,45 @@
import request from '@/utils/axios';
// 获取轮播
export function bannerGet(params?: any) {
return request({
url: '/manager/workplace/banner',
method: 'get',
params
});
}
// 新增轮播
export function bannerPost(data: any) {
return request({
url: '/manager/workplace/banner',
method: 'post',
data
});
}
// 编辑轮播
export function bannerPut(data: any, banner_no: string) {
return request({
url: `/manager/workplace/banners/${banner_no}`,
method: 'put',
data
});
}
// 删除轮播
export function bannerDelete(banner_no: string) {
return request({
url: `/manager/workplace/banners/${banner_no}`,
method: 'delete'
});
}
// 轮播排序
export function bannerReorderPut(data: any) {
return request({
url: `/manager/workplace/banner/reorder`,
method: 'put',
data
});
}

View File

@ -0,0 +1,79 @@
import request from '@/utils/axios';
// 获取分类
export function categoryGet(params?: any) {
return request({
url: '/manager/workplace/category',
method: 'get',
params
});
}
// 新增分类
export function categoryPost(data: any) {
return request({
url: '/manager/workplace/category',
method: 'post',
data
});
}
// 分类编辑
export function categoryPut(data: any, category_no: string) {
return request({
url: `/manager/workplace/categorys/${category_no}`,
method: 'put',
data
});
}
// 删除分类
export function categoryDelete(category_no: string) {
return request({
url: `/manager/workplace/categorys/${category_no}`,
method: 'delete'
});
}
// 分类排序
export function categoryReorderPut(data: any) {
return request({
url: '/manager/workplace/category/reorder',
method: 'put',
data
});
}
// 分类获取应用
export function categoryAppGet(category_no: string) {
return request({
url: `/manager/workplace/categorys/${category_no}/app`,
method: 'get'
});
}
// 分类新增应用
export function categoryAppPost(data: any, category_no: string) {
return request({
url: `/manager/workplace/categorys/${category_no}/app`,
method: 'post',
data
});
}
// 分类删除应用
export function categoryAppDelete(category_no: string, app_id: string) {
return request({
url: `/manager/workplace/categorys/${category_no}/apps/${app_id}`,
method: 'delete'
});
}
// 分类应用排序
export function categorysAppsReorderPut(data: any, category_no: string) {
return request({
url: `/manager/workplace/categorys/${category_no}/app/reorder`,
method: 'put',
data
});
}

View File

@ -5,13 +5,31 @@
{{ msg['content'] }}
</span>
<!-- 图片 -->
<img v-else-if="msg.type == 2" class="w-120px" :src="`${url}${msg['url']}`" />
<img
v-else-if="msg.type == 2"
class="w-120px cursor-pointer"
:src="`${url}${msg['url']}`"
@click="previewPicture(`${url}${msg['url']}`, 'image')"
/>
<!-- GIF -->
<img v-else-if="msg.type == 3" class="w-120px" :src="`${url}${msg['url']}`" />
<img
v-else-if="msg.type == 3"
class="w-120px cursor-pointer"
:src="`${url}${msg['url']}`"
@click="previewPicture(`${url}${msg['url']}`, 'image')"
/>
<!-- 语音 -->
<audio v-else-if="msg.type == 4" :src="`${url}${msg['url']}`"></audio>
<!-- 视频 -->
<video v-else-if="msg.type == 5" controls :src="`${url}${msg['url']}`" class="w-220px h-100px"></video>
<video
v-else-if="msg.type == 5"
controls
controlsList="nofullscreen nodownload noplaybackrate noremote footbar"
disablePictureInPicture
:src="`${url}${msg['url']}`"
class="w-220px h-100px cursor-pointer"
@click="previewPicture(`${url}${msg['url']}`, 'image')"
></video>
<!-- 位置 -->
<div v-else-if="msg.type == 6">
<div>位置标题{{ msg['title'] }}</div>
@ -73,6 +91,7 @@
</template>
<script lang="tsx" name="BdMsg" setup>
import { Fancybox } from '@fancyapps/ui';
import '@lottiefiles/lottie-player/dist/tgs-player';
import { BU_DOU_CONFIG } from '@/config';
interface IProps {
@ -80,4 +99,37 @@ interface IProps {
}
defineProps<IProps>();
const url = BU_DOU_CONFIG.APP_URL;
//
const previewPicture = (url: string, type: string) => {
const imgList = [];
imgList.push({ src: url, defaultType: type });
Fancybox.show(imgList, {
Toolbar: {
display: {
left: ['infobar'],
middle: ['zoomIn', 'zoomOut', 'toggle1to1', 'rotateCCW', 'rotateCW', 'flipX', 'flipY'],
right: ['slideshow', 'thumbs', 'close']
}
}
});
};
</script>
<style scoped>
video::-webkit-media-controls-timeline {
display: none;
}
video::-webkit-media-controls-mute-button {
display: none;
}
video::-webkit-media-controls-toggle-closed-captions-button {
display: none;
}
video::-webkit-media-controls-volume-slider {
display: none;
}
video::-webkit-media-controls-fullscreen-button {
display: none;
}
</style>

View File

@ -10,25 +10,6 @@
:class="tabsMenuValue === item.path ? 'chrome-tab_active' : ''"
@click.stop="tabClick(item)"
>
<!-- <div class="chrome-tab__bg">
<svg style="width: 100%; height: 100%">
<defs>
<symbol id="geometry-left" viewBox="0 0 214 36">
<path d="M17 0h197v36H0v-2c4.5 0 9-3.5 9-8V8c0-4.5 3.5-8 8-8z"></path>
</symbol>
<symbol id="geometry-right" viewBox="0 0 214 36"><use xlink:href="#geometry-left"></use></symbol>
<clipPath><rect width="100%" height="100%" x="0"></rect></clipPath>
</defs>
<svg width="51%" height="100%">
<use xlink:href="#geometry-left" width="214" height="36" fill="currentColor"></use>
</svg>
<g transform="scale(-1, 1)">
<svg width="51%" height="100%" x="-100%" y="0">
<use xlink:href="#geometry-right" width="214" height="36" fill="currentColor"></use>
</svg>
</g>
</svg>
</div> -->
<div v-if="item.icon && tabsIcon">
<component :is="'i-bd-add-text'" theme="outline" size="16" class="cursor-pointer" />
</div>

View File

@ -19,6 +19,7 @@ import 'vue-global-api';
import 'element-plus/dist/index.css';
import 'element-plus/theme-chalk/dark/css-vars.css';
import '@icon-park/vue-next/styles/index.css';
import '@fancyapps/ui/dist/fancybox/fancybox.css';
import '@/styles/index.scss';
import 'uno.css';

View File

@ -9,7 +9,7 @@ const home: Menu.MenuOptions = {
isHide: false,
isKeepAlive: true,
isLink: '',
index: 8,
index: 9,
title: '设置'
},
children: [

View File

@ -9,7 +9,7 @@ const home: Menu.MenuOptions = {
isHide: false,
isKeepAlive: true,
isLink: '',
index: 7,
index: 8,
title: '工具'
},
children: [
@ -27,6 +27,20 @@ const home: Menu.MenuOptions = {
title: 'APP升级'
}
},
{
component: '/tool/command',
name: 'toolCommand',
path: '/tool/command',
meta: {
icon: 'i-bd-command',
isAffix: false,
isFull: false,
isHide: false,
isKeepAlive: true,
isLink: '',
title: '口令管理'
}
},
{
component: '/tool/systemrobotmenu',
name: 'toolSystemrobotmenu',

View File

@ -0,0 +1,43 @@
const home: Menu.MenuOptions = {
name: 'tool',
path: '/workplace',
meta: {
icon: 'i-bd-all-application',
isAffix: false,
isFull: false,
isHide: false,
isKeepAlive: true,
isLink: '',
index: 7,
title: '工作台'
},
children: [
{
name: 'workplaceManage',
path: '/workplace/manage',
meta: {
icon: 'i-bd-application',
isAffix: false,
isFull: false,
isHide: false,
isKeepAlive: true,
isLink: '',
title: '应用管理'
}
},
{
name: 'workplaceConfiguration',
path: '/workplace/configuration',
meta: {
icon: 'i-bd-setting-config',
isAffix: false,
isFull: false,
isHide: false,
isKeepAlive: true,
isLink: '',
title: '工作台设置'
}
}
]
};
export default home;

View File

@ -35,8 +35,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -32,8 +32,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -33,7 +33,7 @@
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -35,8 +35,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -34,7 +34,7 @@
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -36,7 +36,7 @@
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -39,7 +39,7 @@
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
@ -198,8 +198,8 @@ const getUserList = () => {
loadTable.value = true;
messageRecordpersonalGet(queryFrom).then((res: any) => {
loadTable.value = false;
tableData.value = res.list;
total.value = res.count;
tableData.value = res?.list ? res?.list : [];
total.value = res?.count ? res?.count : 0;
});
};
@ -225,6 +225,7 @@ const msgDel = (data: any) => {
const formData = {
channel_id: route.query.uid,
channel_type: 1,
from_uid: route.query.touid,
list
};
messageDelete(formData).then((res: any) => {

View File

@ -30,7 +30,7 @@
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -32,8 +32,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -23,8 +23,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -23,8 +23,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -29,8 +29,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -0,0 +1,146 @@
<template>
<el-dialog
:model-value="value"
:width="600"
:align-center="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
:draggable="true"
:z-index="99"
:title="title"
@close="onClose"
>
<el-form :model="formData" label-width="96px">
<el-form-item label="口令">
<el-input v-model="formData.short_url" placeholder="请输入口令" />
</el-form-item>
<el-form-item label="地址">
<el-input v-model="formData.url" placeholder="请输入地址" />
</el-form-item>
</el-form>
<template #footer>
<el-space>
<el-button @click="onClose">取消</el-button>
<el-button type="primary" :loading="loaging" @click="onConfirm">保存</el-button>
</el-space>
</template>
</el-dialog>
</template>
<script lang="ts" name="CommandDialog" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
// API
import { commandPost, commandPut } from '@/api/command';
interface IProps {
value: boolean;
title: string;
type: 'add' | 'edit';
data: object;
}
const props = withDefaults(defineProps<IProps>(), {
value: false,
title: '新增口令',
type: 'add'
});
const content = ref('');
const loaging = ref<boolean>(false);
const formData = ref({
short_url: '',
url: ''
});
const emits = defineEmits<{
(e: 'update:value', item: boolean): void;
(e: 'ok', item: any): void;
}>();
watch(
() => props.value,
(n, _o) => {
if (n && props.type === 'edit') {
formData.value = props.data as any;
}
if (!n) {
formData.value = {
short_url: '',
url: ''
};
}
}
);
//
const onClose = () => {
emits('update:value', false);
};
//
const addBanner = () => {
loaging.value = true;
commandPost(formData.value)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('新增成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const editBanner = () => {
loaging.value = true;
commandPut(formData.value, (props.data as any).id)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('编辑成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const onConfirm = () => {
if (props.type === 'add') {
addBanner();
}
if (props.type === 'edit') {
editBanner();
}
};
</script>
<style lang="scss" scoped>
.bd-uplod {
::v-deep(.el-upload--picture-card) {
height: 78px;
width: 78px;
}
.avatar {
height: 78px;
width: 78px;
}
}
</style>

View File

@ -0,0 +1,220 @@
<template>
<bd-page class="flex-col">
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
<div class="bd-title-left">
<p class="m-0 font-600">口令管理</p>
</div>
<div class="flex items-center h-50px">
<el-form inline>
<el-form-item class="mb-0 !mr-16px">
<el-input v-model="queryFrom.keyword" placeholder="名称" clearable />
</el-form-item>
<el-form-item class="mb-0 !mr-16px">
<el-button type="primary" @click="getTableList">查询</el-button>
</el-form-item>
<el-form-item class="mb-0 !mr-0">
<el-button type="primary" @click="onCommandAddClick">新增口令</el-button>
</el-form-item>
</el-form>
</div>
</div>
<div class="flex-1 overflow-hidden p-12px">
<el-table v-loading="loadTable" :data="tableData" :border="true" style="width: 100%; height: 100%">
<el-table-column type="index" :width="42" :align="'center'" :fixed="'left'">
<template #header>
<i-bd-drag class="cursor-pointer" size="16" />
</template>
</el-table-column>
<el-table-column v-for="item in column" v-bind="item" :key="item.prop">
<template #default="scope">
<template v-if="item.render">
<component :is="item.render" :row="scope.row"> </component>
</template>
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
</el-table>
</div>
<div class="bd-card-footer pl-12px pr-12px mb-12px flex items-center justify-between">
<div></div>
<el-pagination
v-model:current-page="queryFrom.page_index"
v-model:page-size="queryFrom.page_size"
:page-sizes="[15, 20, 30, 50, 100]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</div>
<!-- 口令操作 -->
<CommandDialog
v-model:value="commandDialogValue"
:title="commandDialogTitle"
:type="commandDialogType"
:data="commandDialogData"
@ok="onCommandOk"
/>
</bd-page>
</template>
<route lang="yaml">
meta:
title: 口令管理
isAffix: false
</route>
<script lang="tsx" name="command" setup>
import { ElButton, ElSpace, ElMessage, ElMessageBox } from 'element-plus';
import CommandDialog from './components/CommandDialog.vue';
// API
import { commandGet, commandDelete } from '@/api/command';
/**
* 表格
*/
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'short_url',
label: '口令名称'
},
{
prop: 'url',
label: '地址'
},
{
prop: 'create_at',
label: '创建时间'
},
{
prop: 'operation',
label: '操作',
width: 150,
align: 'center',
render: (scope: any) => {
return (
<ElSpace>
<ElButton type="primary" onClick={() => onCommandEidt(scope.row)}>
编辑
</ElButton>
<ElButton type="danger" onClick={() => onCommandDel(scope.row)}>
删除
</ElButton>
</ElSpace>
);
}
}
]);
const tableData = ref<any[]>([]);
const loadTable = ref<boolean>(false);
//
const total = ref(0);
//
const queryFrom = reactive({
keyword: '',
page_size: 15,
page_index: 1
});
//
const getTableList = () => {
loadTable.value = true;
commandGet(queryFrom)
.then((res: any) => {
loadTable.value = false;
tableData.value = res.list;
total.value = res.count;
})
.catch(() => {
loadTable.value = false;
});
};
// page-size
const onSizeChange = (size: number) => {
queryFrom.page_size = size;
getTableList();
};
// page-size
const onCurrentChange = (current: number) => {
queryFrom.page_index = current;
getTableList();
};
//
const commandDialogValue = ref(false);
const commandDialogTitle = ref('新增口令');
const commandDialogType = ref<'add' | 'edit'>('add');
const onCommandAddClick = () => {
commandDialogTitle.value = '新增口令';
commandDialogType.value = 'add';
commandDialogValue.value = true;
};
//
const commandDialogData = ref({});
const onCommandEidt = (item: any) => {
commandDialogTitle.value = `编辑${item.short_url}`;
commandDialogData.value = item;
commandDialogType.value = 'edit';
commandDialogValue.value = true;
};
//
const onCommandOk = () => {
getTableList();
};
//
const onCommandDel = (item: any) => {
ElMessageBox.confirm(`确定要对该口令吗?`, `操作提示`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
type: 'warning'
})
.then(() => {
commandDelete(item.id)
.then((_res: any) => {
getTableList();
ElMessage({
type: 'success',
message: `口令删除成功!`
});
})
.catch(err => {
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消成功!'
});
});
};
onMounted(() => {
getTableList();
});
</script>
<style lang="scss" scoped>
//
.bd-title {
border-bottom: 1px solid var(--el-card-border-color);
}
</style>

View File

@ -23,8 +23,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -32,8 +32,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -36,8 +36,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -35,8 +35,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>

View File

@ -33,8 +33,8 @@
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop] }}</slot>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
@ -81,7 +81,7 @@ const userStore = useUserStore();
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'name',
label: '用户名',
label: '昵称',
fixed: 'left',
width: 140
},
@ -91,6 +91,11 @@ const column = reactive<Column.ColumnOptions[]>([
fixed: 'left',
width: 120
},
{
prop: 'username',
label: '用户',
width: 120
},
{
prop: 'avatar',
label: '头像',

View File

@ -0,0 +1,192 @@
<template>
<el-dialog
:model-value="value"
:width="780"
:align-center="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
:draggable="true"
:z-index="99"
title="添加应用"
@close="onClose"
>
<div class="h-540px flex-col">
<el-input v-model="queryFrom.keyword" class="mb-12px" placeholder="请输入应用名称" clearable @keyup.enter="onSearch" />
<!-- 表格 -->
<div class="flex-1 overflow-hidden">
<el-table
v-loading="loadTable"
:data="tableData"
row-key="app_id"
:show-header="false"
style="width: 100%; height: 100%"
@selection-change="onSelectionChange"
>
<el-table-column v-for="(col, index) in column" v-bind="col" :key="index">
<template #default="{ row }">
<template v-if="col.render">
<component :is="col.render" :row="row"> </component>
</template>
<template v-else-if="col.formatter">
<slot :name="col.prop" :row="row">{{ col.formatter(row) }}</slot>
</template>
<template v-else-if="col.prop">
<slot :name="col.prop" :row="row">{{ row[col.prop] }}</slot>
</template>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="bd-card-footer mt-12px flex items-center justify-between">
<div></div>
<el-pagination
v-model:current-page="queryFrom.page_index"
v-model:page-size="queryFrom.page_size"
:page-sizes="[15, 20, 30, 50, 100]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</div>
<template #footer>
<el-space>
<el-button @click="onClose">取消</el-button>
<el-button type="primary" :loading="loaging" @click="onConfirm">确定</el-button>
</el-space>
</template>
</el-dialog>
</template>
<script lang="ts" name="AppDialog" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
// API
import { categoryAppPost } from '@/api/workplace/category';
import { appGet } from '@/api/workplace/app';
interface IProps {
value: boolean;
data: {
category_no: string;
};
}
const props = withDefaults(defineProps<IProps>(), {
value: false
});
const loaging = ref<boolean>(false);
const emits = defineEmits<{
(e: 'update:value', item: boolean): void;
(e: 'ok', item: any): void;
}>();
watch(
() => props.value,
(n, _o) => {
props.value = n;
if (n) {
getTableList();
}
}
);
/**
* 表格
*/
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'name',
label: '名称'
},
{
prop: 'description',
label: '描述'
},
{
type: 'selection'
}
]);
const tableData = ref<any[]>([]);
const loadTable = ref<boolean>(false);
const selectionData = ref<string[]>([]);
const queryFrom = reactive({
keyword: '',
page_size: 15,
page_index: 1
});
const total = ref(0);
//
const getTableList = () => {
loadTable.value = true;
appGet(queryFrom)
.then((res: any) => {
loadTable.value = false;
tableData.value = res.list || [];
total.value = res.count || 0;
})
.catch(() => {
loadTable.value = false;
});
};
//
const onSearch = () => {
queryFrom.page_index = 1;
queryFrom.page_size = 15;
getTableList();
};
const onSelectionChange = (val: any[]) => {
const opt: string[] = [];
val.map(item => {
opt.push(item.app_id);
});
selectionData.value = opt;
};
// page-size
const onSizeChange = (size: number) => {
queryFrom.page_size = size;
getTableList();
};
// page-size
const onCurrentChange = (current: number) => {
queryFrom.page_index = current;
getTableList();
};
const onClose = () => {
emits('update:value', false);
};
//
const onConfirm = () => {
if (selectionData.value.length === 0) {
return ElMessage.info('请输入分类!');
}
const fromData = {
app_ids: selectionData.value
};
loaging.value = true;
categoryAppPost(fromData, props.data.category_no)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('新增应用成功!');
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
</script>

View File

@ -0,0 +1,265 @@
<template>
<bd-page class="flex-col !p-0">
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
<div class="bd-title-left"></div>
<div class="flex items-center h-50px">
<el-form inline>
<el-form-item class="mb-0 !mr-0">
<el-button type="primary" @click="onBannerDialogValue">新增轮播</el-button>
</el-form-item>
</el-form>
</div>
</div>
<div class="flex-1 overflow-hidden p-12px">
<el-table v-loading="loadTable" :data="tableData" row-key="banner_no" :border="true" style="width: 100%; height: 100%">
<el-table-column type="index" :width="42" :align="'center'" :fixed="'left'">
<template #header>
<i-bd-drag class="cursor-pointer" size="16" />
</template>
<template #default>
<i-bd-drag class="bd-drag cursor-pointer" size="16" />
</template>
</el-table-column>
<el-table-column v-for="item in column" v-bind="item" :key="item.prop">
<template #default="scope">
<template v-if="item.render">
<component :is="item.render" :row="scope.row"> </component>
</template>
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 轮播 -->
<BannerDialog
v-model:value="bannerDialogValue"
:title="bannerDialogTitle"
:type="bannerDialogType"
:data="bannerDialogData"
@ok="onBannerOk"
/>
</bd-page>
</template>
<script lang="tsx" name="Banner" setup>
import { ElButton, ElSpace, ElMessageBox, ElMessage } from 'element-plus';
import { Fancybox } from '@fancyapps/ui';
import Sortable from 'sortablejs';
import BannerDialog from './BannerDialog.vue';
import { BU_DOU_CONFIG } from '@/config';
// API
import { bannerGet, bannerDelete, bannerReorderPut } from '@/api/workplace/banner';
/**
* 表格
*/
//
const previewPicture = (url: string) => {
const imgList = [];
imgList.push({ src: url });
Fancybox.show(imgList, {
Toolbar: {
display: {
left: ['infobar'],
middle: ['zoomIn', 'zoomOut', 'toggle1to1', 'rotateCCW', 'rotateCW', 'flipX', 'flipY'],
right: ['slideshow', 'thumbs', 'close']
}
}
});
};
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'title',
label: '名称'
},
{
prop: 'cover',
label: '图片',
width: 146,
render: (scope: any) => {
let img_url = '';
if (scope.row['cover']) {
img_url = `${BU_DOU_CONFIG.APP_URL}${scope.row.cover}`;
}
return <img src={img_url} class={'w-120px h-60px cursor-pointer'} onClick={() => previewPicture(img_url)} />;
}
},
{
prop: 'route',
label: '地址'
},
{
prop: 'status',
label: '打开方式',
formatter(row: any) {
return row.status === 0 ? '网页' : '原生';
}
},
{
prop: 'description',
label: '描述'
},
{
prop: 'created_at',
label: '创建时间',
width: 180
},
{
prop: 'operation',
label: '操作',
width: 150,
align: 'center',
render: (scope: any) => {
return (
<ElSpace>
<ElButton type="primary" onClick={() => onBannerEidt(scope.row)}>
编辑
</ElButton>
<ElButton type="danger" onClick={() => onDelBanner(scope.row)}>
删除
</ElButton>
</ElSpace>
);
}
}
]);
const tableData = ref<any[]>([]);
const loadTable = ref<boolean>(false);
//
const queryFrom = reactive({
keyword: '',
page_size: 15,
page_index: 1
});
//
const getTableList = () => {
loadTable.value = true;
bannerGet(queryFrom)
.then((res: any) => {
loadTable.value = false;
tableData.value = res;
})
.catch(() => {
loadTable.value = false;
});
};
//
const bannerDialogValue = ref(false);
const bannerDialogTitle = ref('新增轮播');
const bannerDialogType = ref<'add' | 'edit'>('add');
const onBannerDialogValue = () => {
bannerDialogTitle.value = `新增轮播`;
bannerDialogType.value = 'add';
bannerDialogValue.value = true;
};
//
const bannerDialogData = ref({});
const onBannerEidt = (item: any) => {
bannerDialogTitle.value = `编辑${item.title}`;
bannerDialogData.value = item;
bannerDialogType.value = 'edit';
bannerDialogValue.value = true;
};
//
const onBannerOk = () => {
getTableList();
};
//
const onDelBanner = (item: any) => {
ElMessageBox.confirm(`确定要对该轮播吗?`, `操作提示`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
type: 'warning'
})
.then(() => {
bannerDelete(item.banner_no)
.then((_res: any) => {
getTableList();
ElMessage({
type: 'success',
message: `轮播删除成功!`
});
})
.catch(err => {
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消成功!'
});
});
};
const bannerReorder = (newIndex: string, oldIndex: string) => {
const fromData = {
banner_nos: [newIndex, oldIndex]
};
bannerReorderPut(fromData).then(res => {
if (res.status == 200) {
getTableList();
ElMessage({
type: 'success',
message: `轮播排序成功`
});
} else {
ElMessage({
type: 'info',
message: '轮播排序失败!'
});
}
});
};
// tree
const tableDrop = () => {
Sortable.create(document.querySelector('.el-table__body-wrapper tbody') as HTMLElement, {
handle: '.bd-drag',
animation: 300,
onEnd(evt: any) {
const { newIndex, oldIndex } = evt;
const tablesList = [...tableData.value];
const currRow = tablesList.splice(oldIndex as number, 1)[0];
tablesList.splice(newIndex as number, 0, currRow);
if (oldIndex > newIndex) {
//
bannerReorder(tablesList[newIndex].banner_no, tablesList[oldIndex].banner_no);
} else {
//
bannerReorder(tablesList[oldIndex].banner_no, tablesList[newIndex].banner_no);
}
}
});
};
onMounted(() => {
getTableList();
tableDrop();
});
</script>
<style lang="scss" scoped>
//
.bd-title {
border-bottom: 1px solid var(--el-card-border-color);
}
</style>

View File

@ -0,0 +1,212 @@
<template>
<el-dialog
:model-value="value"
:width="600"
:align-center="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
:draggable="true"
:z-index="99"
:title="title"
@close="onClose"
>
<el-form :model="formData" label-width="96px">
<el-form-item label="标题">
<el-input v-model="formData.title" placeholder="请输入标题" />
</el-form-item>
<el-form-item label="图片">
<el-upload
ref="upload"
class="bd-upload"
:action="actionURL"
list-type="picture-card"
:show-file-list="false"
:headers="headers"
:before-upload="beforeUploadFile"
:on-success="onFileSuccess"
>
<img v-if="formData.cover" :src="`${BU_DOU_CONFIG.APP_URL}${formData.cover}`" class="avatar" />
<el-icon v-else><Plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="打开方式">
<el-radio-group v-model="formData.jump_type">
<el-radio :label="0">网页</el-radio>
<el-radio :label="1">APP</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="地址">
<el-input v-model="formData.route" placeholder="请输入地址" />
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="formData.description"
type="textarea"
:autosize="{ minRows: 6, maxRows: 8 }"
placeholder="请输入描述"
/>
</el-form-item>
</el-form>
<template #footer>
<el-space>
<el-button @click="onClose">取消</el-button>
<el-button type="primary" :loading="loaging" @click="onConfirm">保存</el-button>
</el-space>
</template>
</el-dialog>
</template>
<script lang="ts" name="BannerDialog" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
import { useUserStore } from '@/stores/modules/user';
// API
import { bannerPost, bannerPut } from '@/api/workplace/banner';
import { feileGet } from '@/api/file';
import { BU_DOU_CONFIG } from '@/config';
interface IProps {
value: boolean;
title: string;
type: 'add' | 'edit';
data: object;
}
const userStore = useUserStore();
const props = withDefaults(defineProps<IProps>(), {
value: false,
title: '新增轮播',
type: 'add'
});
const content = ref('');
const loaging = ref<boolean>(false);
const formData = ref({
cover: '',
title: '',
description: '',
route: '',
jump_type: 0
});
const emits = defineEmits<{
(e: 'update:value', item: boolean): void;
(e: 'ok', item: any): void;
}>();
watch(
() => props.value,
(n, _o) => {
if (n && props.type === 'edit') {
formData.value = props.data as any;
}
if (!n) {
formData.value = {
cover: '',
title: '',
description: '',
route: '',
jump_type: 0
};
}
}
);
/**
* 上传图片
*/
const headers = {
token: userStore.token
};
const actionURL = ref('');
//
const beforeUploadFile = async (rawFile: any) => {
const fileData = {
path: `/${rawFile.uid}/${rawFile.name}`,
type: 'report'
};
const res = (await feileGet(fileData)) as any;
if (res.url) {
actionURL.value = res.url;
return true;
} else {
return false;
}
};
//
const onFileSuccess = (response: any, _uploadFile: any) => {
formData.value.cover = response.path;
};
//
const onClose = () => {
emits('update:value', false);
};
//
const addBanner = () => {
loaging.value = true;
bannerPost(formData.value)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('新增成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const editBanner = () => {
loaging.value = true;
const banner_no = (props.data as any).banner_no;
bannerPut(formData.value, banner_no)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('编辑成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const onConfirm = () => {
if (props.type === 'add') {
addBanner();
}
if (props.type === 'edit') {
editBanner();
}
};
</script>
<style lang="scss" scoped>
.bd-upload {
::v-deep(.el-upload--picture-card) {
height: 78px;
width: 78px;
}
.avatar {
height: 78px;
width: 78px;
}
}
</style>

View File

@ -0,0 +1,133 @@
<template>
<el-dialog
:model-value="value"
:width="400"
:align-center="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
:draggable="true"
:z-index="99"
title="新增分类"
@close="onClose"
>
<div>
<el-input v-model="content" placeholder="请输入分类" />
</div>
<template #footer>
<el-space>
<el-button @click="onClose">取消</el-button>
<el-button type="primary" :loading="loaging" @click="onSend">保存</el-button>
</el-space>
</template>
</el-dialog>
</template>
<script lang="ts" name="CategoryDialog" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
// API
import { categoryPost, categoryPut } from '@/api/workplace/category';
interface IProps {
value: boolean;
type: 'add' | 'edit';
data: {
category_no?: string;
name?: string;
sort_num?: number;
};
}
const props = withDefaults(defineProps<IProps>(), {
value: false,
title: '新增分类',
type: 'add'
});
const content = ref('');
const loaging = ref<boolean>(false);
const emits = defineEmits<{
(e: 'update:value', item: boolean): void;
(e: 'ok', item: any): void;
}>();
watch(
() => props.value,
(n, _o) => {
console.log(props.value);
props.value = n;
if (n && props.type == 'edit') {
content.value = props.data?.name || '';
}
if (!n) {
content.value = '';
}
}
);
//
const onClose = () => {
emits('update:value', false);
};
//
const addCategor = () => {
const fromData = {
name: content.value
};
loaging.value = true;
categoryPost(fromData)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('编辑分类成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const editCategor = () => {
const fromData = {
name: content.value
};
loaging.value = true;
const category_no = (props.data as any).category_no;
categoryPut(fromData, category_no)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('编辑分类成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const onSend = () => {
if (!content.value) {
return ElMessage.info('请输入分类!');
}
//
if (props.type === 'add') {
addCategor();
}
//
if (props.type === 'edit') {
editCategor();
}
};
</script>

View File

@ -0,0 +1,447 @@
<template>
<bd-page class="flex !p-0">
<!-- S 左侧分类 -->
<div class="bd-sort min-w-200px max-w-200px h-full box-border flex flex-col">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
<div class="bd-title-left font-500 text-14px">分组</div>
<div class="flex items-center h-50px">
<i-bd-add :size="22" class="cursor-pointer" @click="onCategoryAdd" />
</div>
</div>
<div class="m-12px">
<el-input v-model="keyword" placeholder="搜索分类/按回车键搜索" clearable />
</div>
<div class="flex-1 overflow-hidden">
<el-scrollbar>
<div class="tree-warp p-12px pt-0">
<div
v-for="item in dataTree"
:key="item.category_no"
class="bd-tree-item"
:class="{ 'bd-tree-activate': item.category_no === optTree }"
@click="onOptTreeClick(item.category_no)"
>
<div class="mr-4px">
<i-bd-drag class="bd-drag cursor-pointer" size="14" />
</div>
<div class="flex-1 text">{{ item.name }}</div>
<div class="bd-opt">
<i-bd-editor :size="16" class="cursor-pointer pr-4px" @click.stop="onCategoryEdit(item)" />
<i-bd-delete :size="16" class="cursor-pointer" @click.stop="onCategoryDelete(item)" />
</div>
</div>
</div>
</el-scrollbar>
</div>
</div>
<!-- E 左侧分类 -->
<!-- S 右侧 表格 -->
<div class="flex-1 h-full flex flex-col">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
<div class="bd-title-left"></div>
<div class="flex items-center h-50px">
<el-form inline>
<el-form-item class="mb-0 !mr-0">
<el-button type="primary" @click="appDialogValue = true">新增应用</el-button>
</el-form-item>
</el-form>
</div>
</div>
<!-- 表格 -->
<div class="flex-1 overflow-hidden p-12px">
<el-table v-loading="loadTable" :data="tableData" row-key="app_id" :border="true" style="width: 100%; height: 100%">
<el-table-column type="index" :width="42" :align="'center'" :fixed="'left'">
<template #header>
<i-bd-drag class="cursor-pointer" size="16" />
</template>
<template #default>
<i-bd-drag class="bd-drag cursor-pointer" size="16" />
</template>
</el-table-column>
<el-table-column v-for="item in column" v-bind="item" :key="item.prop">
<template #default="scope">
<template v-if="item.render">
<component :is="item.render" :row="scope.row"> </component>
</template>
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else>
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- E 右侧 表格 -->
<!-- 新增分类 -->
<CategoryDialog
v-model:value="categoryValue"
:type="categoryType"
:title="categoryTitle"
:data="categoryData"
@ok="onCategoryOk"
/>
<!-- 添加应用 -->
<AppDialog v-model:value="appDialogValue" :data="appDialogData" @ok="onAppDialogOk" />
</bd-page>
</template>
<script lang="tsx" name="CustomGroup" setup>
import { ElButton, ElSpace, ElImage, ElMessageBox, ElMessage } from 'element-plus';
import Sortable from 'sortablejs';
import CategoryDialog from './CategoryDialog.vue';
import AppDialog from './AppDialog.vue';
import { BU_DOU_CONFIG } from '@/config';
// API
import {
categoryGet,
categoryDelete,
categoryReorderPut,
categoryAppGet,
categoryAppDelete,
categorysAppsReorderPut
} from '@/api/workplace/category';
interface Tree {
category_no: string;
name: string;
sort_num: number;
}
/**
* 左侧分类
*/
const dataTree = ref<Tree[]>([]);
const optTree = ref('');
const keyword = ref('');
//
const getCategoryData = () => {
categoryGet().then((res: any) => {
if (res.length > 0) {
dataTree.value = res;
if (!optTree.value) {
optTree.value = res[0].category_no;
}
}
});
};
//
const onOptTreeClick = (no: string) => {
optTree.value = no;
};
//
const onCategoryOk = () => {
getCategoryData();
};
//
const categoryReorder = (newIndex: string, oldIndex: string) => {
const fromData = {
category_nos: [newIndex, oldIndex]
};
categoryReorderPut(fromData).then(res => {
if (res.status == 200) {
getCategoryData();
ElMessage({
type: 'success',
message: `分类排序成功`
});
} else {
ElMessage({
type: 'info',
message: '分类排序失败!'
});
}
});
};
//
const treesDrop = () => {
Sortable.create(document.querySelector('.tree-warp') as HTMLElement, {
handle: '.bd-drag',
animation: 300,
onEnd({ newIndex, oldIndex }: any) {
const treesList = [...dataTree.value];
const currRow = treesList.splice(oldIndex as number, 1)[0];
treesList.splice(newIndex as number, 0, currRow);
if (oldIndex > newIndex) {
//
categoryReorder(treesList[newIndex].category_no, treesList[oldIndex].category_no);
} else {
//
categoryReorder(treesList[oldIndex].category_no, treesList[newIndex].category_no);
}
}
});
};
const categoryValue = ref(false);
const categoryTitle = ref('新增分类');
const categoryType = ref<'add' | 'edit'>('add');
const categoryData = ref({});
//
const onCategoryAdd = () => {
categoryValue.value = true;
categoryTitle.value = '新增分类';
categoryType.value = 'add';
categoryData.value = {};
};
//
const onCategoryEdit = (item: any) => {
console.log(item);
categoryValue.value = true;
categoryTitle.value = '编辑分类';
categoryType.value = 'edit';
categoryData.value = { ...item };
};
//
const onCategoryDelete = (item: any) => {
ElMessageBox.confirm(`确定要对该分类吗?`, `操作提示`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
type: 'warning'
})
.then(() => {
categoryDelete(item.category_no)
.then((_res: any) => {
getCategoryData();
ElMessage({
type: 'success',
message: `轮播删除成功!`
});
})
.catch(err => {
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消成功!'
});
});
};
/**
* 添加应用
*/
const appDialogValue = ref(false);
const appDialogData = ref({
category_no: ''
});
watch(
() => optTree.value,
() => {
appDialogData.value = {
category_no: optTree.value
};
queryFrom.category_no = optTree.value;
getTableList();
}
);
//
const onAppDialogOk = () => {
getTableList();
};
/**
* 表格
*/
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'icon',
label: '应用LOGO',
align: 'center',
width: 100,
render: (scope: any) => {
let img_url = '';
if (scope.row['icon']) {
img_url = `${BU_DOU_CONFIG.APP_URL}${scope.row.icon}`;
}
return <ElImage src={img_url} fit={'scale-down'} class={'w-60px h-60px'} />;
}
},
{
prop: 'name',
label: '应用名称',
width: 160
},
{
prop: 'app_id',
label: '应用APP ID',
width: 290
},
{
prop: 'description',
label: '描述'
},
{
prop: 'operation',
label: '操作',
align: 'center',
width: 80,
render: (scope: any) => {
return (
<ElSpace>
<ElButton type="danger" onClick={() => onDelApply(scope.row)}>
删除
</ElButton>
</ElSpace>
);
}
}
]);
const tableData = ref<any[]>([]);
const loadTable = ref<boolean>(false);
//
const queryFrom = reactive({
category_no: ''
});
//
const getTableList = () => {
loadTable.value = true;
categoryAppGet(queryFrom.category_no)
.then((res: any) => {
loadTable.value = false;
tableData.value = res;
})
.catch(() => {
loadTable.value = false;
});
};
//
const onDelApply = (item: any) => {
ElMessageBox.confirm(`确定要对该应用移除吗?`, `操作提示`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
type: 'warning'
})
.then(() => {
categoryAppDelete(optTree.value, item.app_id)
.then((_res: any) => {
getTableList();
ElMessage({
type: 'success',
message: `应用移除成功!`
});
})
.catch(err => {
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消成功!'
});
});
};
const categorysAppsReorder = (new_app_id: string, old_app_id: string) => {
const fromData = {
app_ids: [new_app_id, old_app_id]
};
categorysAppsReorderPut(fromData, queryFrom.category_no).then(res => {
if (res.status == 200) {
getTableList();
ElMessage({
type: 'success',
message: `应用排序成功`
});
} else {
ElMessage({
type: 'info',
message: '应用排序失败!'
});
}
});
};
// table
const tableDrop = () => {
Sortable.create(document.querySelector('.el-table__body-wrapper tbody') as HTMLElement, {
handle: '.bd-drag',
animation: 300,
onEnd({ newIndex, oldIndex }: any) {
const tablesList = [...tableData.value];
const currRow = tablesList.splice(oldIndex as number, 1)[0];
tablesList.splice(newIndex as number, 0, currRow);
tableData.value = tablesList;
if (oldIndex > newIndex) {
//
categorysAppsReorder(tablesList[newIndex].app_id, tablesList[oldIndex].app_id);
} else {
//
categorysAppsReorder(tablesList[oldIndex].app_id, tablesList[newIndex].app_id);
}
}
});
};
/**
* 初始化数据
*/
onMounted(() => {
getCategoryData();
treesDrop();
tableDrop();
});
</script>
<style lang="scss" scoped>
//
.bd-sort {
border-right: 1px solid var(--el-card-border-color);
}
.bd-tree-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
height: 26px;
padding: 0 4px;
cursor: pointer;
border-radius: 4px;
margin-bottom: 4px;
.bd-opt {
display: none;
}
&:hover {
background-color: var(--el-menu-active-bg-color);
color: var(--el-menu-active-color);
.bd-opt {
display: block;
}
}
}
.bd-tree-activate {
background-color: var(--el-menu-active-bg-color);
.text {
color: var(--el-menu-active-color);
}
}
.bd-title {
border-bottom: 1px solid var(--el-card-border-color);
}
</style>

View File

@ -0,0 +1,161 @@
<template>
<bd-page class="flex-col !p-0">
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
<div class="bd-title-left"></div>
<div class="flex items-center h-50px">
<el-form inline>
<el-form-item class="mb-0 !mr-16px">
<el-input v-model="queryFrom.keyword" placeholder="名称" clearable />
</el-form-item>
<el-form-item class="mb-0 !mr-0">
<el-button type="primary">新增推荐应用</el-button>
</el-form-item>
</el-form>
</div>
</div>
<div class="flex-1 overflow-hidden p-12px">
<el-table v-loading="loadTable" :data="tableData" :border="true" style="width: 100%; height: 100%">
<el-table-column type="index" :width="42" :align="'center'" :fixed="'left'">
<template #header>
<i-bd-drag class="cursor-pointer" size="16" />
</template>
<template #default>
<i-bd-drag class="bd-drag cursor-pointer" size="16" />
</template>
</el-table-column>
<el-table-column v-for="item in column" v-bind="item" :key="item.prop">
<template #default="scope">
<template v-if="item.render">
<component :is="item.render" :row="scope.row"> </component>
</template>
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
</el-table>
</div>
<div class="bd-card-footer pl-12px pr-12px mb-12px flex items-center justify-between">
<div></div>
<el-pagination
v-model:current-page="queryFrom.page_index"
v-model:page-size="queryFrom.page_size"
:page-sizes="[15, 20, 30, 50, 100]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</div>
</bd-page>
</template>
<script lang="tsx" name="Recommend" setup>
import { ElButton, ElSpace, ElImage } from 'element-plus';
import Sortable from 'sortablejs';
import { BU_DOU_CONFIG } from '@/config';
/**
* 表格
*/
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'icon',
label: '应用LOGO',
align: 'center',
width: 100,
render: (scope: any) => {
let img_url = '';
if (scope.row['icon']) {
img_url = `${BU_DOU_CONFIG.APP_URL}${scope.row.icon}`;
}
return <ElImage src={img_url} fit={'scale-down'} class={'w-60px h-60px'} />;
}
},
{
prop: 'name',
label: '应用名称',
width: 160
},
{
prop: 'app_id',
label: '应用APP ID',
width: 290
},
{
prop: 'description',
label: '描述'
},
{
prop: 'operation',
label: '操作',
align: 'center',
render: (_scope: any) => {
return (
<ElSpace>
<ElButton type="primary">配置</ElButton>
</ElSpace>
);
}
}
]);
const tableData = ref<any[]>([]);
const loadTable = ref<boolean>(false);
//
const total = ref(0);
//
const queryFrom = reactive({
keyword: '',
page_size: 15,
page_index: 1
});
//
const getTableList = () => {};
// page-size
const onSizeChange = (size: number) => {
queryFrom.page_size = size;
getTableList();
};
// page-size
const onCurrentChange = (current: number) => {
queryFrom.page_index = current;
getTableList();
};
//
// table
const tableDrop = () => {
Sortable.create(document.querySelector('.el-table__body-wrapper tbody') as HTMLElement, {
// draggable: '.bd-drag',
animation: 300,
onEnd({ newIndex, oldIndex }: any) {
const tablesList = [...tableData.value];
const currRow = tablesList.splice(oldIndex as number, 1)[0];
tablesList.splice(newIndex as number, 0, currRow);
tableData.value = tablesList;
}
});
};
onMounted(() => {
tableDrop();
});
</script>
<style lang="scss" scoped>
//
.bd-title {
border-bottom: 1px solid var(--el-card-border-color);
}
</style>

View File

@ -0,0 +1,41 @@
<template>
<bd-page class="flex-col">
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<el-tabs v-model="activeName" class="bd-tabs">
<el-tab-pane v-for="item in tabsData" :key="item.name" :label="item.label" :name="item.name">
<component :is="item.render" v-if="item.name === activeName" />
</el-tab-pane>
</el-tabs>
</div>
</bd-page>
</template>
<route lang="yaml">
meta:
title: 工作台设置
isAffix: false
</route>
<script lang="tsx" setup>
import Banner from './components/Banner.vue';
import CustomGroup from './components/CustomGroup.vue';
const activeName = ref('banner');
const tabsData = reactive([
{
name: 'banner',
label: '轮播',
render: () => {
return <Banner />;
}
},
{
name: 'custom-group',
label: '自定义分组',
render: () => {
return <CustomGroup />;
}
}
]);
</script>

View File

@ -0,0 +1,230 @@
<template>
<el-dialog
:model-value="value"
:width="600"
:align-center="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
:draggable="true"
:z-index="99"
:title="title"
@close="onClose"
>
<el-form :model="formData" label-width="96px">
<el-form-item label="应用名称">
<el-input v-model="formData.name" placeholder="请输入应用名称" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item label="应用图标">
<el-upload
ref="upload"
class="bd-upload"
:action="actionURL"
list-type="picture-card"
:show-file-list="false"
:headers="headers"
:before-upload="beforeUploadFile"
:on-success="onFileSuccess"
>
<img v-if="formData.icon" :src="`${BU_DOU_CONFIG.APP_URL}${formData.icon}`" class="avatar" />
<el-icon v-else><Plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item label="打开方式">
<el-radio-group v-model="formData.jump_type">
<el-radio :label="0">网页</el-radio>
<el-radio :label="1">APP</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="网页地址">
<el-input v-model="formData.web_route" placeholder="请输入网页地址" />
</el-form-item>
<el-form-item label="APP地址">
<el-input v-model="formData.app_route" placeholder="请输入APP地址" />
</el-form-item>
<el-form-item label="付款">
<el-radio-group v-model="formData.is_paid_app">
<el-radio :label="0">免费</el-radio>
<el-radio :label="1">付费</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="应用描述">
<el-input
v-model="formData.description"
type="textarea"
:autosize="{ minRows: 6, maxRows: 8 }"
placeholder="请输入应用描述"
show-word-limit
maxlength="500"
/>
</el-form-item>
</el-form>
<template #footer>
<el-space>
<el-button @click="onClose">取消</el-button>
<el-button type="primary" :loading="loaging" @click="onConfirm">保存</el-button>
</el-space>
</template>
</el-dialog>
</template>
<script lang="ts" name="Apply" setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
import { useUserStore } from '@/stores/modules/user';
// API
import { appPost, appPut } from '@/api/workplace/app';
import { feileGet } from '@/api/file';
import { BU_DOU_CONFIG } from '@/config';
interface IProps {
value: boolean;
title: string;
type: 'add' | 'edit';
data: object;
}
const props = withDefaults(defineProps<IProps>(), {
value: false,
title: '新增应用',
type: 'add'
});
const userStore = useUserStore();
const content = ref('');
const loaging = ref<boolean>(false);
const formData = ref({
icon: '',
name: '',
jump_type: 0,
description: '',
app_route: '',
web_route: '',
is_paid_app: 0
});
const emits = defineEmits<{
(e: 'update:value', item: boolean): void;
(e: 'ok', item: any): void;
}>();
watch(
() => props.value,
(n, _o) => {
props.value = n;
if (n && props.type == 'edit') {
formData.value = props.data as any;
}
if (!n) {
formData.value = {
icon: '',
name: '',
jump_type: 0,
description: '',
app_route: '',
web_route: '',
is_paid_app: 0
};
}
}
);
/**
* 上传图片
*/
const headers = {
token: userStore.token
};
const actionURL = ref('');
//
const beforeUploadFile = async (rawFile: any) => {
const fileData = {
path: `/${rawFile.uid}/${rawFile.name}`,
type: 'report'
};
const res = (await feileGet(fileData)) as any;
if (res.url) {
actionURL.value = res.url;
return true;
} else {
return false;
}
};
//
const onFileSuccess = (response: any, _uploadFile: any) => {
formData.value.icon = response.path;
};
//
const onClose = () => {
emits('update:value', false);
};
//
const addApp = () => {
loaging.value = true;
appPost(formData.value)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('新增成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const editApp = () => {
loaging.value = true;
const app_id = (props.data as any).app_id;
appPut(formData.value, app_id)
.then((res: any) => {
loaging.value = false;
if (res.status == 200) {
ElMessage.success('编辑成功!');
content.value = '';
onClose();
emits('ok', true);
}
})
.catch(err => {
loaging.value = false;
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
};
//
const onConfirm = () => {
//
if (props.type === 'add') {
addApp();
}
//
if (props.type === 'edit') {
editApp();
}
};
</script>
<style lang="scss" scoped>
.bd-upload {
::v-deep(.el-upload--picture-card) {
height: 78px;
width: 78px;
}
.avatar {
height: 78px;
width: 78px;
}
}
</style>

View File

@ -0,0 +1,256 @@
<template>
<bd-page class="flex-col">
<div class="flex-1 el-card border-none flex-col box-border overflow-hidden">
<div class="h-50px pl-12px pr-12px box-border flex items-center justify-between bd-title">
<div class="bd-title-left">
<p class="m-0 font-600">应用管理</p>
</div>
<div class="flex items-center h-50px">
<el-form inline>
<el-form-item class="mb-0 !mr-16px">
<el-input v-model="queryFrom.keyword" placeholder="应用名称" clearable />
</el-form-item>
<el-form-item class="mb-0 !mr-16px">
<el-button type="primary" @click="getTableList">查询</el-button>
</el-form-item>
<el-form-item class="mb-0 !mr-0">
<el-button type="primary" @click="onAppVersionAdd">新增应用</el-button>
</el-form-item>
</el-form>
</div>
</div>
<div class="flex-1 overflow-hidden p-12px">
<el-table v-loading="loadTable" :data="tableData" :border="true" style="width: 100%; height: 100%">
<el-table-column type="index" :width="42" :align="'center'" :fixed="'left'">
<template #header>
<i-bd-setting class="cursor-pointer" size="16" />
</template>
</el-table-column>
<el-table-column v-for="item in column" v-bind="item" :key="item.prop">
<template #default="scope">
<template v-if="item.render">
<component :is="item.render" :row="scope.row"> </component>
</template>
<template v-else-if="item.formatter">
<slot :name="item.prop" :row="scope.row">{{ item.formatter(scope.row) }}</slot>
</template>
<template v-else-if="item.prop">
<slot :name="item.prop" :row="scope.row">{{ scope.row[item.prop!] }}</slot>
</template>
</template>
</el-table-column>
</el-table>
</div>
<div class="bd-card-footer pl-12px pr-12px mb-12px flex items-center justify-between">
<div></div>
<el-pagination
v-model:current-page="queryFrom.page_index"
v-model:page-size="queryFrom.page_size"
:page-sizes="[15, 20, 30, 50, 100]"
:background="true"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="onSizeChange"
@current-change="onCurrentChange"
/>
</div>
</div>
<!-- 应用 -->
<Apply v-model:value="applyAddValue" :type="applyType" :title="applyTitle" :data="applyData" @ok="onApplyClick" />
</bd-page>
</template>
<route lang="yaml">
meta:
title: 应用管理
isAffix: false
</route>
<script lang="tsx" setup>
import { ElButton, ElMessageBox, ElMessage, ElSpace } from 'element-plus';
import { Fancybox } from '@fancyapps/ui';
import Apply from './components/Apply.vue';
import { BU_DOU_CONFIG } from '@/config';
// API
import { appGet, appDelete } from '@/api/workplace/app';
/**
* 新增应用
*/
//
const applyAddValue = ref<boolean>(false);
const applyTitle = ref('新增应用');
const applyType = ref<'add' | 'edit'>('add');
const onAppVersionAdd = () => {
applyAddValue.value = true;
applyTitle.value = '新增应用';
applyType.value = 'add';
};
//
const onApplyClick = () => {
getTableList();
};
const previewPicture = (url: string) => {
const imgList = [];
imgList.push({ src: url });
Fancybox.show(imgList, {
Toolbar: {
display: {
left: ['infobar'],
middle: ['zoomIn', 'zoomOut', 'toggle1to1', 'rotateCCW', 'rotateCW', 'flipX', 'flipY'],
right: ['slideshow', 'thumbs', 'close']
}
}
});
};
/**
* 表格
*/
const column = reactive<Column.ColumnOptions[]>([
{
prop: 'icon',
label: '应用LOGO',
align: 'center',
width: 100,
render: (scope: any) => {
let img_url = '';
if (scope.row['icon']) {
img_url = `${BU_DOU_CONFIG.APP_URL}${scope.row.icon}`;
}
return <img src={img_url} class={'w-60px h-60px cursor-pointer'} onClick={() => previewPicture(img_url)} />;
}
},
{
prop: 'name',
label: '应用名称',
width: 160
},
{
prop: 'app_id',
label: '应用APP ID',
width: 290
},
{
prop: 'status',
label: '应用状态',
width: 100,
formatter(row: any) {
return row.status === 1 ? '开启' : '关闭';
}
},
{
prop: 'description',
label: '应用描述'
},
{
prop: 'operation',
label: '操作',
width: 150,
align: 'center',
render: (scope: any) => {
return (
<ElSpace>
<ElButton type="primary" onClick={() => oApplyEidt(scope.row)}>
编辑
</ElButton>
<ElButton type="danger" onClick={() => onDelApply(scope.row)}>
删除
</ElButton>
</ElSpace>
);
}
}
]);
const tableData = ref<any[]>([]);
const loadTable = ref<boolean>(false);
//
const total = ref(0);
//
const queryFrom = reactive({
keyword: '',
page_size: 15,
page_index: 1
});
//
const getTableList = () => {
loadTable.value = true;
appGet(queryFrom)
.then((res: any) => {
loadTable.value = false;
tableData.value = res.list || [];
total.value = res.count || 0;
})
.catch(() => {
loadTable.value = false;
});
};
//
const applyData = ref({});
const oApplyEidt = (item: any) => {
applyTitle.value = `编辑${item.name}`;
applyData.value = item;
applyType.value = 'edit';
applyAddValue.value = true;
};
//
const onDelApply = (item: any) => {
ElMessageBox.confirm(`确定要对该应用删除吗?`, `操作提示`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false,
type: 'warning'
})
.then(() => {
appDelete(item.app_id)
.then((_res: any) => {
getTableList();
ElMessage({
type: 'success',
message: `应用删除成功!`
});
})
.catch(err => {
if (err.status == 400) {
ElMessage.error(err.msg);
}
});
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消成功!'
});
});
};
// page-size
const onSizeChange = (size: number) => {
queryFrom.page_size = size;
getTableList();
};
// page-size
const onCurrentChange = (current: number) => {
queryFrom.page_index = current;
getTableList();
};
onMounted(() => {
getTableList();
});
</script>
<style lang="scss" scoped>
//
.bd-title {
border-bottom: 1px solid var(--el-card-border-color);
}
</style>

View File

@ -11,6 +11,7 @@
/* 当前页面最大化 css */
.main-maximize {
.aside-split,
.el-aside,
.el-header,
@ -40,6 +41,7 @@
border-radius: 0;
box-shadow: none;
}
.table-search {
padding: 18px 0 0 !important;
margin-bottom: 0 !important;
@ -52,18 +54,22 @@
flex-direction: column;
align-items: center;
height: 100%;
.text {
margin: 20px 0 30px;
font-size: 23px;
font-weight: bold;
color: var(--el-text-color-regular);
}
.el-descriptions {
width: 100%;
padding: 40px 0 0;
.el-descriptions__title {
font-size: 18px;
}
.el-descriptions__label {
width: 200px;
}
@ -75,6 +81,7 @@
display: flex;
width: 100%;
height: 100%;
.table-box {
// 这里减去的是 treeFilter 组件宽度
width: calc(100% - 230px);
@ -94,8 +101,9 @@
.table-search {
padding: 18px 18px 0;
margin-bottom: 10px;
.el-form {
.el-form-item__content > * {
.el-form-item__content>* {
width: 100%;
}
@ -104,6 +112,7 @@
padding: 0 10px;
}
}
.operation {
display: flex;
align-items: center;
@ -117,9 +126,11 @@
.header-button-lf {
float: left;
}
.header-button-ri {
float: right;
}
.el-button {
margin-bottom: 15px;
}
@ -133,6 +144,7 @@
table {
width: 100%;
}
.el-table__header th {
height: 45px;
font-size: 15px;
@ -140,16 +152,18 @@
color: var(--el-text-color-primary);
background: var(--el-fill-color-light);
}
.el-table__row {
height: 45px;
font-size: 14px;
.el-table__placeholder {
display: inline;
}
}
// 设置 el-table header 文字不换行并省略
.el-table__header .el-table__cell > .cell {
.el-table__header .el-table__cell>.cell {
white-space: nowrap;
}
@ -159,6 +173,7 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.table-empty {
line-height: 30px;
}
@ -186,16 +201,19 @@
height: 40px !important;
font-size: 14px !important;
}
.el-table__row {
height: 40px !important;
font-size: 13px !important;
}
}
.el-table--large {
.el-table__header th {
height: 50px !important;
font-size: 16px !important;
}
.el-table__row {
height: 50px !important;
font-size: 15px !important;
@ -208,12 +226,14 @@
padding: 16px 20px;
margin-bottom: 0;
border-bottom: 1px solid var(--el-border-color-lighter);
span {
font-size: 17px;
line-height: 17px;
color: var(--el-text-color-primary) !important;
}
}
.el-drawer__footer {
border-top: 1px solid var(--el-border-color-lighter);
}
@ -227,8 +247,10 @@
.drawer-multiColumn-form {
display: flex;
flex-wrap: wrap;
.el-form-item {
width: 47%;
&:nth-child(2n-1) {
margin-right: 5%;
}
@ -242,6 +264,7 @@
padding: 15px 20px;
margin: 0;
border-bottom: 1px solid var(--el-border-color-lighter);
.el-dialog__title {
font-size: 17px;
}
@ -258,12 +281,47 @@
background: var(--el-fill-color-light);
}
}
/* el-menu-item */
.el-menu-item {
height: 48px !important;
line-height: 48px !important;
}
/* bu-button*/
.bu-button:focus-visible {
outline: 0;
}
/* bd-tabs */
.bd-tabs {
display: flex;
flex-direction: column;
height: 100%;
.el-tabs__header {
margin: 0;
}
.el-tabs__nav-wrap {
padding: 0 24px;
&::after {
height: 1px;
}
}
.el-tabs__item {
padding: 0 12px;
}
.el-tabs__content {
flex: 1;
height: 100% !important;
}
.el-tab-pane {
flex: 1;
height: 100% !important;
}
}

View File

@ -80,8 +80,8 @@ type ObjToKeyValArray<T> = {
}[keyof T];
declare namespace Column {
interface ColumnOptions {
prop: string;
label: string;
prop?: string;
label?: string;
type?: 'selection' | 'index' | 'expand';
fixed?: true | 'left' | 'right';
width?: string | number;