Merge branch 'main' into dev

This commit is contained in:
RockYang
2024-03-30 11:57:31 +08:00
31 changed files with 543 additions and 105 deletions

View File

@@ -173,7 +173,7 @@ const routes = [
name: 'admin-manger',
meta: {title: '管理员'},
component: () => import('@/views/admin/Manager.vue'),
},
}
]
},

View File

@@ -313,12 +313,16 @@ httpGet("/api/config/get?key=system").then(res => {
// 获取系统公告
httpGet("/api/config/get?key=notice").then(res => {
notice.value = md.render(res.data['content'])
const oldNotice = localStorage.getItem(noticeKey.value);
// 如果公告有更新,则显示公告
if (oldNotice !== notice.value && notice.value.length > 10) {
showNotice.value = true
try {
notice.value = md.render(res.data['content'])
const oldNotice = localStorage.getItem(noticeKey.value);
// 如果公告有更新,则显示公告
if (oldNotice !== notice.value && notice.value.length > 10) {
showNotice.value = true
}
} catch (e) {
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})

View File

@@ -7,14 +7,10 @@
</div>
<ul class="nav-items">
<li v-for="item in navs" :key="item.path">
<!-- <el-tooltip effect="light" :content="item.title" placement="right">-->
<!-- -->
<!-- </el-tooltip>-->
<a @click="changeNav(item)" :class="item.path === curPath ? 'active' : ''">
<el-image :src="item.icon_path" :width="20" v-if="item.icon_path"/>
<i :class="'iconfont icon-' + item.icon" v-else></i>
<el-image :src="item.icon" :width="20"/>
</a>
<div :class="item.path === curPath ? 'title active' : 'title'">{{ item.title }}</div>
<div :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</div>
</li>
</ul>
</div>
@@ -37,21 +33,12 @@ import {ElMessage} from "element-plus";
const router = useRouter();
const logo = ref('/images/logo.png');
const navs = ref([
{path: "/chat", icon_path: "/images/chat.png", title: "对话聊天"},
{path: "/mj", icon_path: "/images/mj.png", title: "MJ 绘画"},
{path: "/sd", icon_path: "/images/sd.png", title: "SD 绘画"},
{path: "/apps", icon: "menu", title: "应用中心"},
{path: "/images-wall", icon: "image-list", title: "作品展示"},
{path: "/powerLog", icon: "log", title: "消费日志"},
{path: "/member", icon: "vip-user", title: "会员计划"},
{path: "/invite", icon: "share", title: "推广计划"},
])
const navs = ref([])
const curPath = ref(router.currentRoute.value.path)
const changeNav = (item) => {
curPath.value = item.path
router.push(item.path)
curPath.value = item.url
router.push(item.url)
}
onMounted(() => {
@@ -60,6 +47,12 @@ onMounted(() => {
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
// 获取菜单
httpGet("/api/menu/list").then(res => {
navs.value = res.data
}).catch(e => {
ElMessage.error("获取系统菜单失败:" + e.message)
})
})
</script>

View File

@@ -0,0 +1,247 @@
<template>
<div class="container menu" v-loading="loading">
<div class="handle-box">
<el-button type="primary" :icon="Plus" @click="add">新增</el-button>
</div>
<el-row>
<el-table :data="items" :row-key="row => row.id" table-layout="auto">
<el-table-column prop="name" label="菜单名称">
<template #default="scope">
<span class="sort" :data-id="scope.row.id">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column prop="icon" label="菜单图标">
<template #default="scope">
<el-image class="menu-icon" :src="scope.row.icon"/>
</template>
</el-table-column>
<el-table-column prop="url" label="菜单URL"/>
<el-table-column prop="enabled" label="启用状态">
<template #default="scope">
<el-switch v-model="scope.row['enabled']" @change="enable(scope.row)"/>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" type="primary" @click="edit(scope.row)">编辑</el-button>
<el-popconfirm title="确定要删除当前记录吗?" @confirm="remove(scope.row)" :width="200">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</el-row>
<el-dialog
v-model="showDialog"
:title="title"
:close-on-click-modal="false"
>
<el-form :model="item" label-width="120px" ref="formRef" :rules="rules">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="item.name" autocomplete="off"/>
</el-form-item>
<el-form-item label="菜单图标" prop="icon">
<el-input v-model="item.icon" placeholder="菜单图标地址">
<template #append>
<el-upload
:auto-upload="true"
:show-file-list="false"
:http-request="uploadImg"
>
<el-icon class="uploader-icon">
<UploadFilled/>
</el-icon>
</el-upload>
</template>
</el-input>
</el-form-item>
<el-form-item label="菜单URL" prop="url">
<el-input v-model="item.url" autocomplete="off"/>
</el-form-item>
<el-form-item label="启用状态" prop="enable">
<el-switch v-model="item.enabled"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">取消</el-button>
<el-button type="primary" @click="save">提交</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {onMounted, reactive, ref} from "vue";
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage} from "element-plus";
import {dateFormat, removeArrayItem} from "@/utils/libs";
import {Plus, UploadFilled} from "@element-plus/icons-vue";
import {Sortable} from "sortablejs";
import Compressor from "compressorjs";
// 变量定义
const items = ref([])
const item = ref({})
const showDialog = ref(false)
const title = ref("")
const rules = reactive({
name: [{required: true, message: '请输入菜单名称', trigger: 'change',}],
icon: [{required: true, message: '请上传菜单图标', trigger: 'change',}],
url: [{required: true, message: '请输入菜单地址', trigger: 'change',}],
})
const loading = ref(true)
const formRef = ref(null)
const fetchData = () => {
// 获取数据
httpGet('/api/admin/menu/list').then((res) => {
if (res.data) {
// 初始化数据
const arr = res.data;
for (let i = 0; i < arr.length; i++) {
arr[i].last_used_at = dateFormat(arr[i].last_used_at)
}
items.value = arr
}
loading.value = false
}).catch(() => {
ElMessage.error("获取数据失败");
})
}
onMounted(() => {
const drawBodyWrapper = document.querySelector('.el-table__body tbody')
fetchData()
// 初始化拖动排序插件
Sortable.create(drawBodyWrapper, {
sort: true,
animation: 500,
onEnd({newIndex, oldIndex, from}) {
if (oldIndex === newIndex) {
return
}
const sortedData = Array.from(from.children).map(row => row.querySelector('.sort').getAttribute('data-id'));
const ids = []
const sorts = []
sortedData.forEach((id, index) => {
ids.push(parseInt(id))
sorts.push(index)
})
httpPost("/api/admin/menu/sort", {ids: ids, sorts: sorts}).catch(e => {
ElMessage.error("排序失败" + e.message)
})
}
})
})
const add = function () {
title.value = "新增菜单"
showDialog.value = true
item.value = {}
}
const edit = function (row) {
title.value = "修改菜单"
showDialog.value = true
item.value = row
}
const save = function () {
formRef.value.validate((valid) => {
if (valid) {
showDialog.value = false
if (!item.value.id) {
item.value.sort_num = items.value.length + 1
}
httpPost('/api/admin/menu/save', item.value).then(() => {
ElMessage.success('操作成功!')
fetchData()
}).catch((e) => {
ElMessage.error('操作失败,' + e.message)
})
} else {
return false
}
})
}
const enable = (row) => {
httpPost('/api/admin/menu/enable', {id: row.id, enabled: row.enabled}).then(() => {
ElMessage.success("操作成功")
}).catch(e => {
ElMessage.error("操作失败" + e.message)
})
}
const remove = function (row) {
httpGet('/api/admin/menu/remove?id=' + row.id).then(() => {
ElMessage.success("删除成功")
items.value = removeArrayItem(items.value, row, (v1, v2) => {
return v1.id === v2.id
})
}).catch((e) => {
ElMessage.error("删除失败" + e.message)
})
}
// 图片上传
const uploadImg = (file) => {
// 压缩图片并上传
new Compressor(file.file, {
quality: 0.6,
success(result) {
const formData = new FormData();
formData.append('file', result, result.name);
// 执行上传操作
httpPost('/api/admin/upload', formData).then((res) => {
item.value.icon = res.data.url
ElMessage.success('上传成功')
}).catch((e) => {
ElMessage.error('上传失败:' + e.message)
})
},
error(e) {
ElMessage.error('上传失败:' + e.message)
},
});
};
</script>
<style lang="stylus" scoped>
.menu {
.opt-box {
padding-bottom: 10px;
display flex;
justify-content flex-end
.el-icon {
margin-right: 5px;
}
}
.menu-icon {
width 36px
height 36px
}
.el-select {
width: 100%
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="container list" v-loading="loading">
<div class="container product" v-loading="loading">
<div class="handle-box">
<el-button type="primary" :icon="Plus" @click="add">新增</el-button>
@@ -153,13 +153,13 @@ onMounted(() => {
})
const add = function () {
title.value = "新增模型"
title.value = "新增产品"
showDialog.value = true
item.value = {}
}
const edit = function (row) {
title.value = "修改模型"
title.value = "修改产品"
showDialog.value = true
item.value = row
}
@@ -206,7 +206,7 @@ const remove = function (row) {
</script>
<style lang="stylus" scoped>
.list {
.product {
.opt-box {
padding-bottom: 10px;

View File

@@ -253,6 +253,10 @@
<el-button type="primary" @click="save('notice')">保存</el-button>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="菜单配置" name="menu">
<Menu/>
</el-tab-pane>
</el-tabs>
</div>
</template>
@@ -265,6 +269,7 @@ import {ElMessage} from "element-plus";
import {InfoFilled, UploadFilled} from "@element-plus/icons-vue";
import MdEditor from "md-editor-v3";
import 'md-editor-v3/lib/style.css';
import Menu from "@/views/admin/Menu.vue";
const activeName = ref('basic')
const system = ref({models: []})