stylus 语法换成 saas 语法全部完成

This commit is contained in:
GeekMaster
2025-08-02 10:24:10 +08:00
parent 54f8494b5c
commit 92915f7678
24 changed files with 845 additions and 790 deletions

View File

@@ -1,3 +1,5 @@
@use 'waterfall-list.scss' as *;
.page-dall { .page-dall {
// background-color: #282c34; // background-color: #282c34;
@@ -211,7 +213,6 @@
} }
// 任务列表 // 任务列表
@import 'waterfall-list.scss';
} }
.no-more-data { .no-more-data {

View File

@@ -1,3 +1,5 @@
@use 'running-job-list.scss' as *;
.page-mj { .page-mj {
// background-color: #282c34; // background-color: #282c34;
height: 100%; height: 100%;
@@ -323,7 +325,6 @@
.job-list-box { .job-list-box {
// 任务列表 // 任务列表
@import 'running-job-list.scss';
.finish-job-list { .finish-job-list {
#waterfall { #waterfall {

View File

@@ -1,3 +1,6 @@
@use 'waterfall-list.scss' as *;
@use 'sd-task-dialog.scss' as *;
.page-sd { .page-sd {
// background-color: #282c34; // background-color: #282c34;
@@ -212,7 +215,6 @@
} }
// 任务列表 // 任务列表
@import 'waterfall-list.scss';
} }
.no-more-data { .no-more-data {
@@ -222,8 +224,6 @@
} }
} }
@import 'sd-task-dialog.scss';
.mj-list-item-prompt { .mj-list-item-prompt {
.el-icon { .el-icon {
margin-left: 10px; margin-left: 10px;

View File

@@ -1,3 +1,5 @@
@use 'sd-task-dialog.scss' as *;
@keyframes expandUp { @keyframes expandUp {
0% { 0% {
transform: scaleY(0); transform: scaleY(0);
@@ -79,7 +81,8 @@
} }
.iconfont { .iconfont {
} }
.el-icon, .iconfont { .el-icon,
.iconfont {
top: 2px; top: 2px;
cursor: pointer; cursor: pointer;
color: #fff !important; color: #fff !important;
@@ -123,6 +126,4 @@
} }
} }
} }
}
@use "sd-task-dialog.scss" as *;
}

View File

@@ -1,3 +1,5 @@
@use 'model-select.scss' as *;
.mobile-chat-list { .mobile-chat-list {
.content { .content {
padding-top: 46px; padding-top: 46px;
@@ -32,5 +34,3 @@
} }
} }
} }
@use "model-select.scss" as *;

View File

@@ -1,3 +1,5 @@
@use 'model-select.scss' as *;
.mobile-chat { .mobile-chat {
.van-nav-bar { .van-nav-bar {
position: static; position: static;
@@ -84,5 +86,3 @@
// } // }
// } // }
// } // }
@use "model-select.scss" as *;

View File

@@ -1,6 +1,6 @@
.job-list-box { @use 'running-job-list.scss' as *;
@import 'running-job-list.scss';
.job-list-box {
.finish-job-list { .finish-job-list {
#waterfall { #waterfall {
display: flex; display: flex;

View File

@@ -361,32 +361,8 @@
</el-main> </el-main>
</el-container> </el-container>
<el-dialog v-model="showNotice" :show-close="true" class="notice-dialog" title="网站公告">
<div class="notice">
<div v-html="notice"></div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="notShow" type="primary">我知道了不再显示</el-button>
</span>
</template>
</el-dialog>
<ChatSetting :show="showChatSetting" @hide="showChatSetting = false" /> <ChatSetting :show="showChatSetting" @hide="showChatSetting = false" />
<!-- <el-dialog
v-model="showConversationDialog"
title="实时语音通话"
:before-close="hangUp"
>
<realtime-conversation
@close="showConversationDialog = false"
ref="conversationRef"
:height="dialogHeight + 'px'"
/>
</el-dialog> -->
<el-dialog v-model="showConversationDialog" title="实时语音通话" :fullscreen="true"> <el-dialog v-model="showConversationDialog" title="实时语音通话" :fullscreen="true">
<div v-loading="!frameLoaded"> <div v-loading="!frameLoaded">
<iframe <iframe
@@ -426,8 +402,7 @@ import { fetchEventSource } from '@microsoft/fetch-event-source'
import Clipboard from 'clipboard' import Clipboard from 'clipboard'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import 'highlight.js/styles/a11y-dark.css' import 'highlight.js/styles/a11y-dark.css'
import MarkdownIt from 'markdown-it'
import emoji from 'markdown-it-emoji'
import { computed, nextTick, onMounted, ref, watch } from 'vue' import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { getUserToken } from '../store/session' import { getUserToken } from '../store/session'
@@ -453,9 +428,7 @@ const isLogin = ref(false)
const showHello = ref(true) const showHello = ref(true)
const inputRef = ref(null) const inputRef = ref(null)
const textHeightRef = ref(null) const textHeightRef = ref(null)
const showNotice = ref(false)
const notice = ref('')
const noticeKey = ref('SYSTEM_NOTICE')
const store = useSharedStore() const store = useSharedStore()
const row = ref(1) const row = ref(1)
const showChatSetting = ref(false) const showChatSetting = ref(false)
@@ -651,30 +624,6 @@ getSystemInfo()
ElMessage.error('获取系统配置失败:' + e.message) ElMessage.error('获取系统配置失败:' + e.message)
}) })
const md = new MarkdownIt({
breaks: true,
html: true,
linkify: true,
typographer: true,
}).use(emoji)
// 获取系统公告
httpGet('/api/config/get?key=notice')
.then((res) => {
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) {
console.warn(e)
}
})
.catch((e) => {
ElMessage.error('获取系统配置失败:' + e.message)
})
// 获取工具函数 // 获取工具函数
httpGet('/api/function/list') httpGet('/api/function/list')
.then((res) => { .then((res) => {
@@ -1318,11 +1267,6 @@ const getModelValue = (model_id) => {
return '' return ''
} }
const notShow = () => {
localStorage.setItem(noticeKey.value, notice.value)
showNotice.value = false
}
const files = ref([]) const files = ref([])
// 插入文件 // 插入文件
const insertFile = (file) => { const insertFile = (file) => {
@@ -1367,33 +1311,7 @@ const realtimeChat = () => {
<style lang="scss"> <style lang="scss">
@use '@/assets/css/markdown/vue.css' as *; @use '@/assets/css/markdown/vue.css' as *;
.notice-dialog { @use 'sass:color';
.el-dialog__header {
padding-bottom: 0;
}
.el-dialog__body {
padding: 0 20px;
h2 {
margin: 20px 0 15px 0;
}
ol,
ul {
padding-left: 10px;
}
ol {
list-style: decimal-leading-zero;
padding-left: 20px;
}
ul {
list-style: inside;
}
}
}
.input-container { .input-container {
.el-textarea { .el-textarea {
@@ -1452,7 +1370,7 @@ const realtimeChat = () => {
margin-left: auto; margin-left: auto;
&:hover { &:hover {
color: darken(#f56c6c, 10%); color: color.adjust(#f56c6c, $lightness: -10%);
} }
} }
} }

View File

@@ -78,6 +78,19 @@
</div> </div>
<footer-bar /> <footer-bar />
<!-- 网站公告对话框 -->
<el-dialog v-model="showNotice" :show-close="true" class="notice-dialog" title="网站公告">
<div class="notice">
<div v-html="notice"></div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="notShow" type="primary">我知道了不再显示</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -88,6 +101,8 @@ import { checkSession, getLicenseInfo, getSystemInfo } from '@/store/cache'
import { removeUserToken } from '@/store/session' import { removeUserToken } from '@/store/session'
import { httpGet } from '@/utils/http' import { httpGet } from '@/utils/http'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import MarkdownIt from 'markdown-it'
import emoji from 'markdown-it-emoji'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
@@ -103,6 +118,19 @@ const githubURL = ref(import.meta.env.VITE_GITHUB_URL)
const giteeURL = ref(import.meta.env.VITE_GITEE_URL) const giteeURL = ref(import.meta.env.VITE_GITEE_URL)
const navs = ref([]) const navs = ref([])
// 公告相关变量
const showNotice = ref(false)
const notice = ref('')
const noticeKey = ref('SYSTEM_NOTICE')
// Markdown 解析器
const md = new MarkdownIt({
breaks: true,
html: true,
linkify: true,
typographer: true,
}).use(emoji)
onMounted(() => { onMounted(() => {
getSystemInfo() getSystemInfo()
.then((res) => { .then((res) => {
@@ -135,14 +163,68 @@ onMounted(() => {
isLogin.value = true isLogin.value = true
}) })
.catch(() => {}) .catch(() => {})
// 获取系统公告
httpGet('/api/config/get?key=notice')
.then((res) => {
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) {
console.warn(e)
}
})
.catch((e) => {
ElMessage.error('获取系统配置失败:' + e.message)
})
}) })
const logout = () => { const logout = () => {
removeUserToken() removeUserToken()
router.push('/login') router.push('/login')
} }
// 不再显示公告
const notShow = () => {
localStorage.setItem(noticeKey.value, notice.value)
showNotice.value = false
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use '../assets/css/index.scss' as *; @use '../assets/css/index.scss' as *;
</style> </style>
<style lang="scss">
.notice-dialog {
.el-dialog__header {
padding-bottom: 0;
}
.el-dialog__body {
padding: 0 20px;
h2 {
margin: 20px 0 15px 0;
}
ol,
ul {
padding-left: 10px;
}
ol {
list-style: decimal-leading-zero;
padding-left: 20px;
}
ul {
list-style: inside;
}
}
}
</style>

View File

@@ -529,7 +529,7 @@
</div> </div>
<!-- 视频预览对话框 --> <!-- 视频预览对话框 -->
<el-dialog v-model="store.showDialog" title="视频预览" width="70%" center> <el-dialog v-model="store.showDialog" title="视频预览" center>
<video <video
:src="store.currentVideoUrl" :src="store.currentVideoUrl"
autoplay="true" autoplay="true"

View File

@@ -291,36 +291,36 @@ const changePreset = (row) => {
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.list { .list {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
.handle-input { .handle-input {
max-width 150px; max-width: 150px;
margin-right 10px; margin-right: 10px;
} }
} }
.copy-key { .copy-key {
margin-left 5px margin-left: 5px;
cursor pointer cursor: pointer;
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.pagination { .pagination {
padding 20px 0 padding: 20px 0;
display flex display: flex;
justify-content righ justify-content: right;
} }
} }
.el-form { .el-form {
.el-form-item__content { .el-form-item__content {
.info { .info {
color #999999 color: #999999;
} }
.el-icon { .el-icon {
padding-left: 10px; padding-left: 10px;

View File

@@ -5,7 +5,7 @@
</div> </div>
<el-row> <el-row>
<el-table :data="items" :row-key="row => row.id" table-layout="auto"> <el-table :data="items" :row-key="(row) => row.id" table-layout="auto">
<el-table-column type="selection" width="38"></el-table-column> <el-table-column type="selection" width="38"></el-table-column>
<el-table-column prop="name" label="分类名称"> <el-table-column prop="name" label="分类名称">
<template #default="scope"> <template #default="scope">
@@ -17,13 +17,17 @@
</el-table-column> </el-table-column>
<el-table-column label="图标" prop="icon"> <el-table-column label="图标" prop="icon">
<template #default="scope"> <template #default="scope">
<el-image v-if="scope.row.icon" :src="scope.row.icon" style="width: 45px; height: 45px; border-radius: 50%"/> <el-image
v-if="scope.row.icon"
:src="scope.row.icon"
style="width: 45px; height: 45px; border-radius: 50%"
/>
<el-tag type="info" v-else>无图标</el-tag> <el-tag type="info" v-else>无图标</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="enabled" label="启用状态"> <el-table-column prop="enabled" label="启用状态">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row['enabled']" @change="enableSet(scope.row)"/> <el-switch v-model="scope.row['enabled']" @change="enableSet(scope.row)" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="180"> <el-table-column label="操作" width="180">
@@ -40,23 +44,19 @@
</el-row> </el-row>
<el-dialog <el-dialog
v-model="showDialog" v-model="showDialog"
:title="title" :title="title"
:close-on-click-modal="false" :close-on-click-modal="false"
style="width: 90%; max-width: 600px;" style="width: 90%; max-width: 600px"
> >
<el-form :model="item" label-width="120px" ref="formRef" :rules="rules"> <el-form :model="item" label-width="120px" ref="formRef" :rules="rules">
<el-form-item label="分类名称" prop="name"> <el-form-item label="分类名称" prop="name">
<el-input v-model="item.name" autocomplete="off"/> <el-input v-model="item.name" autocomplete="off" />
</el-form-item> </el-form-item>
<el-form-item label="应用图标" prop="icon"> <el-form-item label="应用图标" prop="icon">
<el-input v-model="item.icon"> <el-input v-model="item.icon">
<template #append> <template #append>
<el-upload <el-upload :auto-upload="true" :show-file-list="false" :http-request="uploadImg">
:auto-upload="true"
:show-file-list="false"
:http-request="uploadImg"
>
上传 上传
</el-upload> </el-upload>
</template> </template>
@@ -78,34 +78,37 @@
</template> </template>
<script setup> <script setup>
import {onMounted, onUnmounted, reactive, ref} from "vue"; import { httpGet, httpPost } from '@/utils/http'
import {httpGet, httpPost} from "@/utils/http"; import { removeArrayItem } from '@/utils/libs'
import {ElMessage} from "element-plus"; import { Plus } from '@element-plus/icons-vue'
import {removeArrayItem} from "@/utils/libs"; import Compressor from 'compressorjs'
import {Sortable} from "sortablejs"; import { ElMessage } from 'element-plus'
import Compressor from "compressorjs"; import { Sortable } from 'sortablejs'
import { onMounted, reactive, ref } from 'vue'
// 变量定义 // 变量定义
const items = ref([]) const items = ref([])
const item = ref({}) const item = ref({})
const showDialog = ref(false) const showDialog = ref(false)
const title = ref("") const title = ref('')
const rules = reactive({ const rules = reactive({
name: [{required: true, message: '请输入分类名称', trigger: 'change',}], name: [{ required: true, message: '请输入分类名称', trigger: 'change' }],
}) })
const loading = ref(true) const loading = ref(true)
const formRef = ref(null) const formRef = ref(null)
// 获取数据 // 获取数据
const fetchData = () => { const fetchData = () => {
httpGet('/api/admin/app/type/list').then((res) => { httpGet('/api/admin/app/type/list')
if (res.data) { .then((res) => {
items.value = res.data if (res.data) {
} items.value = res.data
loading.value = false }
}).catch(() => { loading.value = false
ElMessage.error("获取数据失败"); })
}) .catch(() => {
ElMessage.error('获取数据失败')
})
} }
onMounted(() => { onMounted(() => {
@@ -116,12 +119,14 @@ onMounted(() => {
Sortable.create(drawBodyWrapper, { Sortable.create(drawBodyWrapper, {
sort: true, sort: true,
animation: 500, animation: 500,
onEnd({newIndex, oldIndex, from}) { onEnd({ newIndex, oldIndex, from }) {
if (oldIndex === newIndex) { if (oldIndex === newIndex) {
return return
} }
const sortedData = Array.from(from.children).map(row => row.querySelector('.sort').getAttribute('data-id')); const sortedData = Array.from(from.children).map((row) =>
row.querySelector('.sort').getAttribute('data-id')
)
const ids = [] const ids = []
const sorts = [] const sorts = []
sortedData.forEach((id, index) => { sortedData.forEach((id, index) => {
@@ -130,22 +135,23 @@ onMounted(() => {
items.value[index].sort_num = index + 1 items.value[index].sort_num = index + 1
}) })
httpPost("/api/admin/app/type/sort", {ids: ids, sorts: sorts}).then(() => { httpPost('/api/admin/app/type/sort', { ids: ids, sorts: sorts })
}).catch(e => { .then(() => {})
ElMessage.error("排序失败" + e.message) .catch((e) => {
}) ElMessage.error('排序失败:' + e.message)
} })
},
}) })
}) })
const add = function () { const add = function () {
title.value = "新增分类" title.value = '新增分类'
showDialog.value = true showDialog.value = true
item.value = { enabled: true, } item.value = { enabled: true }
} }
const edit = function (row) { const edit = function (row) {
title.value = "修改分类" title.value = '修改分类'
showDialog.value = true showDialog.value = true
item.value = row item.value = row
} }
@@ -157,12 +163,14 @@ const save = function () {
} }
if (valid) { if (valid) {
showDialog.value = false showDialog.value = false
httpPost('/api/admin/app/type/save', item.value).then(() => { httpPost('/api/admin/app/type/save', item.value)
ElMessage.success('操作成功!') .then(() => {
fetchData() ElMessage.success('操作成功!')
}).catch((e) => { fetchData()
ElMessage.error('操作失败,' + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败,' + e.message)
})
} else { } else {
return false return false
} }
@@ -171,23 +179,26 @@ const save = function () {
// 设置启用状态 // 设置启用状态
const enableSet = (row) => { const enableSet = (row) => {
httpPost('/api/admin/app/type/enable', {id: row.id, enabled: row.enabled}).then(() => { httpPost('/api/admin/app/type/enable', { id: row.id, enabled: row.enabled })
ElMessage.success("操作成功") .then(() => {
}).catch(e => { ElMessage.success('操作成功!')
ElMessage.error("操作失败" + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败:' + e.message)
})
} }
// 删除数据 // 删除数据
const remove = function (row) { const remove = function (row) {
httpGet('/api/admin/app/type/remove?id=' + row.id).then(() => { httpGet('/api/admin/app/type/remove?id=' + row.id)
ElMessage.success("删除成功") .then(() => {
items.value = removeArrayItem(items.value, row, (v1, v2) => { ElMessage.success('删除成功!')
return v1.id === v2.id items.value = removeArrayItem(items.value, row, (v1, v2) => {
return v1.id === v2.id
})
})
.catch((e) => {
ElMessage.error('删除失败:' + e.message)
}) })
}).catch((e) => {
ElMessage.error("删除失败" + e.message)
})
} }
// 图片上传 // 图片上传
@@ -196,19 +207,21 @@ const uploadImg = (file) => {
new Compressor(file.file, { new Compressor(file.file, {
quality: 0.6, quality: 0.6,
success(result) { success(result) {
const formData = new FormData(); const formData = new FormData()
formData.append('file', result, result.name); formData.append('file', result, result.name)
// 执行上传操作 // 执行上传操作
httpPost('/api/admin/upload', formData).then((res) => { httpPost('/api/admin/upload', formData)
item.value.icon = res.data.url .then((res) => {
ElMessage.success('上传成功') item.value.icon = res.data.url
}).catch((e) => { ElMessage.success('上传成功')
ElMessage.error('上传失败:' + e.message) })
}) .catch((e) => {
ElMessage.error('上传失败:' + e.message)
})
}, },
error(e) { error(e) {
ElMessage.error('上传失败:' + e.message) ElMessage.error('上传失败:' + e.message)
}, },
}); })
}; }
</script> </script>

View File

@@ -392,22 +392,21 @@ const remove = function (row) {
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
@use "../../assets/css/admin/form.scss" as *; @use '../../assets/css/admin/form.scss' as *;
.model-list { .model-list {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
.handle-input { .handle-input {
max-width 150px; max-width: 150px;
margin-right 10px; margin-right: 10px;
} }
} }
.cell { .cell {
.copy-model { .copy-model {
margin-left 6px margin-left: 6px;
cursor pointer cursor: pointer;
} }
} }
@@ -419,22 +418,21 @@ const remove = function (row) {
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.sort { .sort {
cursor move cursor: move;
.iconfont { .iconfont {
position relative position: relative;
top 1px top: 1px;
} }
} }
.pagination { .pagination {
padding 20px 0 padding: 20px 0;
display flex display: flex;
justify-content right justify-content: right;
} }
} }
</style> </style>

View File

@@ -10,17 +10,19 @@
<span class="sort" :data-id="scope.row.id">{{ scope.row.name }}</span> <span class="sort" :data-id="scope.row.id">{{ scope.row.name }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="函数别名" prop="label"/> <el-table-column label="函数别名" prop="label" />
<el-table-column label="功能描述" prop="description"/> <el-table-column label="功能描述" prop="description" />
<el-table-column label="启用状态"> <el-table-column label="启用状态">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row.enabled" @change="functionSet('enabled',scope.row)"/> <el-switch v-model="scope.row.enabled" @change="functionSet('enabled', scope.row)" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="150" align="right"> <el-table-column label="操作" width="150" align="right">
<template #default="scope"> <template #default="scope">
<el-button size="small" type="primary" @click="rowEdit(scope.$index, scope.row)">编辑</el-button> <el-button size="small" type="primary" @click="rowEdit(scope.$index, scope.row)"
>编辑</el-button
>
<el-popconfirm title="确定要删除当前函数吗?" @confirm="remove(scope.row)" :width="200"> <el-popconfirm title="确定要删除当前函数吗?" @confirm="remove(scope.row)" :width="200">
<template #reference> <template #reference>
<el-button size="small" type="danger">删除</el-button> <el-button size="small" type="danger">删除</el-button>
@@ -31,34 +33,18 @@
</el-table> </el-table>
</el-row> </el-row>
<el-dialog <el-dialog v-model="showDialog" :title="title" :close-on-click-modal="false" width="50%">
v-model="showDialog"
:title="title"
:close-on-click-modal="false"
width="50%"
>
<el-form :model="item" label-width="120px" ref="formRef" label-position="left" :rules="rules"> <el-form :model="item" label-width="120px" ref="formRef" label-position="left" :rules="rules">
<el-form-item label="函数名称:" prop="name"> <el-form-item label="函数名称:" prop="name">
<el-input <el-input v-model="item.name" placeholder="函数名称最好为英文" autocomplete="off" />
v-model="item.name"
placeholder="函数名称最好为英文"
autocomplete="off"
/>
</el-form-item> </el-form-item>
<el-form-item label="函数标签:" prop="label"> <el-form-item label="函数标签:" prop="label">
<el-input <el-input v-model="item.label" placeholder="函数的中文名称" autocomplete="off" />
v-model="item.label"
placeholder="函数的中文名称"
autocomplete="off"
/>
</el-form-item> </el-form-item>
<el-form-item label="功能描述:" prop="description"> <el-form-item label="功能描述:" prop="description">
<el-input <el-input v-model="item.description" autocomplete="off" />
v-model="item.description"
autocomplete="off"
/>
</el-form-item> </el-form-item>
<el-form-item label="函数参数:" prop="parameters"> <el-form-item label="函数参数:" prop="parameters">
@@ -66,10 +52,7 @@
<el-table :data="params" :border="childBorder" size="small"> <el-table :data="params" :border="childBorder" size="small">
<el-table-column label="参数名称" width="120"> <el-table-column label="参数名称" width="120">
<template #default="scope"> <template #default="scope">
<el-input <el-input v-model="scope.row.name" autocomplete="off" />
v-model="scope.row.name"
autocomplete="off"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="参数类型" width="120"> <el-table-column label="参数类型" width="120">
@@ -81,17 +64,14 @@
</el-table-column> </el-table-column>
<el-table-column label="参数描述"> <el-table-column label="参数描述">
<template #default="scope"> <template #default="scope">
<el-input <el-input v-model="scope.row.desc" autocomplete="off" />
v-model="scope.row.desc"
autocomplete="off"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="必填参数" width="80"> <el-table-column label="必填参数" width="80">
<template #default="scope"> <template #default="scope">
<div class="param-opt"> <div class="param-opt">
<el-checkbox v-model="scope.row.required"/> <el-checkbox v-model="scope.row.required" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -99,7 +79,13 @@
<el-table-column label="操作" width="80"> <el-table-column label="操作" width="80">
<template #default="scope"> <template #default="scope">
<div class="param-opt"> <div class="param-opt">
<el-button type="danger" :icon="Delete" circle @click="removeParam(scope.$index)" size="small"/> <el-button
type="danger"
:icon="Delete"
circle
@click="removeParam(scope.$index)"
size="small"
/>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -108,7 +94,7 @@
<div class="param-line"> <div class="param-line">
<el-button type="primary" @click="addParam" size="small"> <el-button type="primary" @click="addParam" size="small">
<el-icon> <el-icon>
<Plus/> <Plus />
</el-icon> </el-icon>
增加参数 增加参数
</el-button> </el-button>
@@ -118,25 +104,21 @@
<el-form-item label="API 地址:" prop="action"> <el-form-item label="API 地址:" prop="action">
<el-input <el-input
v-model="item.action" v-model="item.action"
autocomplete="off" autocomplete="off"
placeholder="该函数实现的API地址可以是第三方服务API" placeholder="该函数实现的API地址可以是第三方服务API"
/> />
</el-form-item> </el-form-item>
<el-form-item label="API Token" prop="token"> <el-form-item label="API Token" prop="token">
<el-input <el-input v-model="item.token" autocomplete="off" placeholder="API授权Token">
v-model="item.token"
autocomplete="off"
placeholder="API授权Token"
>
<template #append> <template #append>
<el-tooltip <el-tooltip
class="box-item" class="box-item"
effect="dark" effect="dark"
content="只有本地服务才可以使用自动生成Token<br/>第三方服务请填写第三方服务API Token" content="只有本地服务才可以使用自动生成Token<br/>第三方服务请填写第三方服务API Token"
placement="top-end" placement="top-end"
raw-content raw-content
> >
<el-button @click="generateToken">生成Token</el-button> <el-button @click="generateToken">生成Token</el-button>
</el-tooltip> </el-tooltip>
@@ -144,27 +126,26 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label="启用状态"> <el-form-item label="启用状态">
<el-switch v-model="item.enabled"/> <el-switch v-model="item.enabled" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="showDialog = false">取消</el-button> <el-button @click="showDialog = false">取消</el-button>
<el-button type="primary" @click="save">保存</el-button> <el-button type="primary" @click="save">保存</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { httpGet, httpPost } from '@/utils/http'
import {Delete, Plus} from "@element-plus/icons-vue"; import { arrayContains, copyObj } from '@/utils/libs'
import {onMounted, reactive, ref} from "vue"; import { Delete, Plus } from '@element-plus/icons-vue'
import {httpGet, httpPost} from "@/utils/http"; import { ElMessage } from 'element-plus'
import {ElMessage} from "element-plus"; import { onMounted, reactive, ref } from 'vue'
import {arrayContains, copyObj} from "@/utils/libs";
const showDialog = ref(false) const showDialog = ref(false)
const parentBorder = ref(true) const parentBorder = ref(true)
@@ -174,35 +155,36 @@ const item = ref({})
const params = ref([]) const params = ref([])
const formRef = ref(null) const formRef = ref(null)
const loading = ref(true) const loading = ref(true)
const title = ref("新增函数") const title = ref('新增函数')
const rules = reactive({ const rules = reactive({
name: [{required: true, message: '请输入函数名称', trigger: 'blur',}], name: [{ required: true, message: '请输入函数名称', trigger: 'blur' }],
label: [{required: true, message: '请输入函数标签', trigger: 'blur',}], label: [{ required: true, message: '请输入函数标签', trigger: 'blur' }],
description: [{required: true, message: '请输入函数功能描述', trigger: 'blur',}], description: [{ required: true, message: '请输入函数功能描述', trigger: 'blur' }],
}) })
const paramsType = ref(["string", "number"]) const paramsType = ref(['string', 'number'])
onMounted(() => { onMounted(() => {
fetch() fetch()
}) })
const fetch = () => { const fetch = () => {
httpGet('/api/admin/function/list').then((res) => { httpGet('/api/admin/function/list')
if (res.data) { .then((res) => {
tableData.value = res.data if (res.data) {
} tableData.value = res.data
loading.value = false }
}).catch(() => { loading.value = false
ElMessage.error("获取数据失败"); })
}) .catch(() => {
ElMessage.error('获取数据失败')
})
} }
// 编辑 // 编辑
const curIndex = ref(0) const curIndex = ref(0)
const rowEdit = function (index, row) { const rowEdit = function (index, row) {
title.value = "编辑函数" title.value = '编辑函数'
curIndex.value = index curIndex.value = index
item.value = copyObj(row) item.value = copyObj(row)
// initialize parameters // initialize parameters
@@ -214,7 +196,7 @@ const rowEdit = function (index, row) {
name: key, name: key,
type: props[key].type, type: props[key].type,
desc: props[key].description, desc: props[key].description,
required: arrayContains(required, key) required: arrayContains(required, key),
}) })
} }
params.value = _params params.value = _params
@@ -222,7 +204,7 @@ const rowEdit = function (index, row) {
} }
const addRow = function () { const addRow = function () {
item.value = {enabled: true} item.value = { enabled: true }
params.value = [] params.value = []
showDialog.value = true showDialog.value = true
} }
@@ -235,104 +217,114 @@ const save = function () {
const required = [] const required = []
// process params // process params
for (let i = 0; i < params.value.length; i++) { for (let i = 0; i < params.value.length; i++) {
properties[params.value[i].name] = {"type": params.value[i].type, "description": params.value[i].desc} properties[params.value[i].name] = {
type: params.value[i].type,
description: params.value[i].desc,
}
if (params.value[i].required) { if (params.value[i].required) {
required.push(params.value[i].name) required.push(params.value[i].name)
} }
} }
item.value.parameters = {type: "object", "properties": properties, required: required} item.value.parameters = { type: 'object', properties: properties, required: required }
httpPost('/api/admin/function/save', item.value).then((res) => { httpPost('/api/admin/function/save', item.value)
ElMessage.success('操作成功') .then((res) => {
console.log(res.data) ElMessage.success('操作成功')
if (item.value.id > 0) { console.log(res.data)
tableData.value[curIndex.value] = item.value if (item.value.id > 0) {
} else { tableData.value[curIndex.value] = item.value
tableData.value.push(res.data) } else {
} tableData.value.push(res.data)
}).catch((e) => { }
ElMessage.error('操作失败,' + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败,' + e.message)
})
} }
}) })
} }
const remove = function (row) { const remove = function (row) {
httpGet('/api/admin/function/remove?id=' + row.id).then(() => { httpGet('/api/admin/function/remove?id=' + row.id)
ElMessage.success("删除成功!") .then(() => {
fetch() ElMessage.success('删除成功!')
}).catch(() => { fetch()
ElMessage.error("删除失败!") })
}) .catch(() => {
ElMessage.error('删除失败!')
})
} }
const addParam = function () { const addParam = function () {
if (!params.value) { if (!params.value) {
item.value = [] item.value = []
} }
params.value.push({name: "", type: "string", desc: "", required: false}) params.value.push({ name: '', type: 'string', desc: '', required: false })
} }
const removeParam = function (index) { const removeParam = function (index) {
params.value.splice(index, 1); params.value.splice(index, 1)
} }
const functionSet = (filed, row) => { const functionSet = (filed, row) => {
httpPost('/api/admin/function/set', {id: row.id, filed: filed, value: row[filed]}).then(() => { httpPost('/api/admin/function/set', { id: row.id, filed: filed, value: row[filed] })
ElMessage.success("操作成功!") .then(() => {
}).catch(e => { ElMessage.success('操作成功!')
ElMessage.error("操作失败:" + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败:' + e.message)
})
} }
const generateToken = () => { const generateToken = () => {
httpGet('/api/admin/function/token').then(res => { httpGet('/api/admin/function/token')
ElMessage.success("生成 Token 成功") .then((res) => {
item.value.token = res.data ElMessage.success('生成 Token 成功')
}).catch(() => { item.value.token = res.data
ElMessage.error("生成 Token 失败") })
}) .catch(() => {
ElMessage.error('生成 Token 失败')
})
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.function { .function {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
} }
.param-line { .param-line {
padding 5px 0 padding: 5px 0;
.el-icon { .el-icon {
margin-right 5px; margin-right: 5px;
} }
} }
.param-opt { .param-opt {
display flex display: flex;
justify-content center justify-content: center;
.el-icon { .el-icon {
font-size: 20px; font-size: 20px;
margin-top 5px; margin-top: 5px;
margin-left 5px; margin-left: 5px;
cursor pointer cursor: pointer;
} }
} }
.el-input--small { .el-input--small {
width 30px; width: 30px;
.el-input__inner { .el-input__inner {
text-align center text-align: center;
} }
} }
.pagination { .pagination {
padding 20px 0 padding: 20px 0;
display flex display: flex;
justify-content right justify-content: right;
} }
} }
</style> </style>

View File

@@ -52,6 +52,6 @@ watch(
) )
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
@use "../../assets/css/main.scss" as *; @use '../../assets/css/main.scss' as *;
</style> </style>

View File

@@ -123,107 +123,108 @@ const doLogin = function (verifyData) {
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.bg { .bg {
position absolute position: absolute;
left 0 left: 0;
right 0 right: 0;
top 0 top: 0;
bottom 0 bottom: 0;
background #8d4bbb background: #8d4bbb;
// background-image url("~@/assets/img/transparent-bg.png") // background-image url("~@/assets/img/transparent-bg.png")
// background-repeat:repeat; // background-repeat:repeat;
background-image url("@/assets/img/admin-login-bg.jpg") background-image: url('@/assets/img/admin-login-bg.jpg');
background-size cover background-size: cover;
background-position center background-position: center;
background-repeat no-repeat background-repeat: no-repeat;
filter: blur(10px); /* 调整模糊程度,可以根据需要修改值 */ filter: blur(10px); /* 调整模糊程度,可以根据需要修改值 */
z-index 0 z-index: 0;
} }
.admin-login { .admin-login {
position absolute position: absolute;
left 0 left: 0;
top 0 top: 0;
z-index 10 z-index: 10;
display flex display: flex;
justify-content center justify-content: center;
width: 100% width: 100%;
height: 100vh height: 100vh;
.main { .main {
max-width 400px max-width: 400px;
display flex display: flex;
justify-content center justify-content: center;
align-items center align-items: center;
height 100vh height: 100vh;
.contain { .contain {
width 100% width: 100%;
padding 40px; padding: 40px;
color #ffffff color: #ffffff;
border-radius 10px; border-radius: 10px;
background rgba(0, 0, 0, 0.4) background: rgba(0, 0, 0, 0.4);
.logo { .logo {
text-align center text-align: center;
.el-image { .el-image {
width 120px; width: 120px;
cursor pointer cursor: pointer;
border-radius 50% border-radius: 50%;
} }
} }
.header { .header {
width 100% width: 100%;
//margin-bottom 20px //margin-bottom 20px
padding 10px padding: 10px;
font-size 26px font-size: 26px;
text-align center text-align: center;
} }
.content { .content {
width 100% width: 100%;
height: auto height: auto;
border-radius 3px border-radius: 3px;
.el-input { .el-input {
margin 10px 0 margin: 10px 0;
} }
.block { .block {
margin-bottom 16px margin-bottom: 16px;
.el-input__inner { .el-input__inner {
border 1px solid $gray-v6 !important border: 1px solid #dcdfe6 !important;
.el-icon-user, .el-icon-lock { .el-icon-user,
font-size 20px .el-icon-lock {
font-size: 20px;
} }
} }
} }
.btn-row { .btn-row {
padding-top 10px; padding-top: 10px;
.login-btn { .login-btn {
width 100% width: 100%;
font-size 16px font-size: 16px;
letter-spacing 2px letter-spacing: 2px;
} }
} }
.text-line { .text-line {
justify-content center justify-content: center;
padding-top 10px; padding-top: 10px;
font-size 14px; font-size: 14px;
} }
} }
} }
.foot-container { .foot-container {
background rgba(0, 0, 0, 0.3); background: rgba(0, 0, 0, 0.3);
--text-color: #ffffff; --text-color: #ffffff;
} }
} }

View File

@@ -63,22 +63,20 @@ const fetchList = function (_page, _pageSize) {
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.list { .list {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.pagination { .pagination {
margin-top 20px margin-top: 20px;
display flex display: flex;
justify-content center justify-content: center;
} }
} }
</style> </style>

View File

@@ -1,14 +1,13 @@
<template> <template>
<div class="container list" v-loading="loading"> <div class="container list" v-loading="loading">
<div class="handle-box"> <div class="handle-box">
<el-button type="primary" :icon="Plus" @click="add">新增</el-button> <el-button type="primary" :icon="Plus" @click="add">新增</el-button>
</div> </div>
<el-row> <el-row>
<el-table :data="items" :row-key="row => row.id" table-layout="auto"> <el-table :data="items" :row-key="(row) => row.id" table-layout="auto">
<el-table-column prop="username" label="用户名"/> <el-table-column prop="username" label="用户名" />
<el-table-column prop="last_login_ip" label="最后登录IP"/> <el-table-column prop="last_login_ip" label="最后登录IP" />
<el-table-column label="最后登录时间"> <el-table-column label="最后登录时间">
<template #default="scope"> <template #default="scope">
@@ -17,7 +16,7 @@
</el-table-column> </el-table-column>
<el-table-column prop="enabled" label="启用状态"> <el-table-column prop="enabled" label="启用状态">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row['status']" @change="enable(scope.row)"/> <el-switch v-model="scope.row['status']" @change="enable(scope.row)" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间"> <el-table-column label="创建时间">
@@ -28,7 +27,9 @@
<el-table-column label="操作" width="180"> <el-table-column label="操作" width="180">
<template #default="scope"> <template #default="scope">
<el-button size="small" type="primary" @click="resetPass(scope.row)">重置密码</el-button> <el-button size="small" type="primary" @click="resetPass(scope.row)"
>重置密码</el-button
>
<el-popconfirm title="确定要删除当前记录吗?" @confirm="remove(scope.row)" :width="200"> <el-popconfirm title="确定要删除当前记录吗?" @confirm="remove(scope.row)" :width="200">
<template #reference> <template #reference>
<el-button size="small" type="danger">删除</el-button> <el-button size="small" type="danger">删除</el-button>
@@ -39,64 +40,62 @@
</el-table> </el-table>
</el-row> </el-row>
<el-dialog <el-dialog v-model="showDialog" title="添加用户" :close-on-click-modal="false">
v-model="showDialog"
title="添加用户"
:close-on-click-modal="false"
>
<el-form :model="item" label-width="120px" ref="formRef" :rules="rules"> <el-form :model="item" label-width="120px" ref="formRef" :rules="rules">
<el-form-item label="用户名" prop="username"> <el-form-item label="用户名" prop="username">
<el-input v-model="item.username" autocomplete="off"/> <el-input v-model="item.username" autocomplete="off" />
</el-form-item> </el-form-item>
<el-form-item label="密码" prop="password"> <el-form-item label="密码" prop="password">
<el-input v-model="item.password" type="password" autocomplete="off"/> <el-input v-model="item.password" type="password" autocomplete="off" />
</el-form-item> </el-form-item>
<el-form-item label="重复密码" prop="repass"> <el-form-item label="重复密码" prop="repass">
<el-input v-model="item.repass" type="password" autocomplete="off"/> <el-input v-model="item.repass" type="password" autocomplete="off" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="showDialog = false">取消</el-button> <el-button @click="showDialog = false">取消</el-button>
<el-button type="primary" @click="save">提交</el-button> <el-button type="primary" @click="save">提交</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import {onMounted, reactive, ref} from "vue"; import { httpGet, httpPost } from '@/utils/http'
import {httpGet, httpPost} from "@/utils/http"; import { dateFormat, removeArrayItem } from '@/utils/libs'
import {ElMessage, ElMessageBox} from "element-plus"; import { Plus } from '@element-plus/icons-vue'
import {dateFormat, removeArrayItem} from "@/utils/libs"; import { ElMessage, ElMessageBox } from 'element-plus'
import {Plus} from "@element-plus/icons-vue"; import { onMounted, reactive, ref } from 'vue'
// 变量定义 // 变量定义
const items = ref([]) const items = ref([])
const item = ref({}) const item = ref({})
const showDialog = ref(false) const showDialog = ref(false)
const title = ref("") const title = ref('')
const loading = ref(true) const loading = ref(true)
const formRef = ref(null) const formRef = ref(null)
const rules = reactive({ const rules = reactive({
username: [{required: true, message: '请输入用户名', trigger: 'change',}], username: [{ required: true, message: '请输入用户名', trigger: 'change' }],
password: [{required: true, message: '请输入密码', trigger: 'change',}], password: [{ required: true, message: '请输入密码', trigger: 'change' }],
repass: [{required: true, message: '请再次输入密码', trigger: 'change',}], repass: [{ required: true, message: '请再次输入密码', trigger: 'change' }],
}) })
// 获取数据 // 获取数据
const fetchData = () => { const fetchData = () => {
httpGet('/api/admin/list').then((res) => { httpGet('/api/admin/list')
items.value = res.data .then((res) => {
loading.value = false items.value = res.data
}).catch(() => { loading.value = false
ElMessage.error("获取数据失败"); })
}) .catch(() => {
ElMessage.error('获取数据失败')
})
} }
onMounted(() => { onMounted(() => {
@@ -113,33 +112,37 @@ const resetPass = function (row) {
ElMessageBox.prompt('请输入新密码', '重置密码', { ElMessageBox.prompt('请输入新密码', '重置密码', {
confirmButtonText: '确认', confirmButtonText: '确认',
cancelButtonText: '取消', cancelButtonText: '取消',
}).then(({value}) => {
httpPost("/api/admin/resetPass", {
id: row.id,
password: value
}).then(() => {
ElMessage.success("操作成功")
}).catch(e => {
ElMessage.error("操作失败" + e.message)
})
}).catch(() => {
}) })
.then(({ value }) => {
httpPost('/api/admin/resetPass', {
id: row.id,
password: value,
})
.then(() => {
ElMessage.success('操作成功')
})
.catch((e) => {
ElMessage.error('操作失败:' + e.message)
})
})
.catch(() => {})
} }
const save = function () { const save = function () {
formRef.value.validate((valid) => { formRef.value.validate((valid) => {
if (item.value.password !== item.value.repass) { if (item.value.password !== item.value.repass) {
return ElMessage.error("两次输入密码不一致") return ElMessage.error('两次输入密码不一致')
} }
if (valid) { if (valid) {
showDialog.value = false showDialog.value = false
httpPost('/api/admin/save', item.value).then((res) => { httpPost('/api/admin/save', item.value)
ElMessage.success('操作成功!') .then((res) => {
fetchData() ElMessage.success('操作成功!')
}).catch((e) => { fetchData()
ElMessage.error('操作失败,' + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败,' + e.message)
})
} else { } else {
return false return false
} }
@@ -147,41 +150,43 @@ const save = function () {
} }
const enable = (row) => { const enable = (row) => {
httpPost('/api/admin/enable', {id: row.id, enabled: row.status}).then(() => { httpPost('/api/admin/enable', { id: row.id, enabled: row.status })
ElMessage.success("操作成功") .then(() => {
}).catch(e => { ElMessage.success('操作成功!')
ElMessage.error("操作失败" + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败:' + e.message)
})
} }
const remove = function (row) { const remove = function (row) {
httpGet('/api/admin/remove?id=' + row.id).then(() => { httpGet('/api/admin/remove?id=' + row.id)
ElMessage.success("删除成功") .then(() => {
items.value = removeArrayItem(items.value, row, (v1, v2) => { ElMessage.success('删除成功!')
return v1.id === v2.id items.value = removeArrayItem(items.value, row, (v1, v2) => {
return v1.id === v2.id
})
})
.catch((e) => {
ElMessage.error('删除失败:' + e.message)
}) })
}).catch((e) => {
ElMessage.error("删除失败" + e.message)
})
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.list { .list {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.pagination { .pagination {
padding 20px 0 padding: 20px 0;
display flex display: flex;
justify-content right justify-content: right;
} }
} }
</style> </style>

View File

@@ -252,29 +252,28 @@ const uploadImg = (file) => {
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
@use "../../assets/css/admin/form.scss" as *; @use '../../assets/css/admin/form.scss' as *;
@use "../../assets/css/main.scss" as *; @use '../../assets/css/main.scss' as *;
.menu { .menu {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
} }
.menu-icon { .menu-icon {
width 36px width: 36px;
height 36px height: 36px;
} }
.sort { .sort {
cursor move cursor: move;
.iconfont { .iconfont {
position relative position: relative;
top 1px top: 1px;
} }
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.el-table .cell { .el-table .cell {

View File

@@ -3,7 +3,12 @@
<div class="handle-box"> <div class="handle-box">
<el-input v-model="query.order_no" placeholder="订单号" class="handle-input mr10"></el-input> <el-input v-model="query.order_no" placeholder="订单号" class="handle-input mr10"></el-input>
<el-select v-model="query.status" placeholder="订单状态" style="width: 100px"> <el-select v-model="query.status" placeholder="订单状态" style="width: 100px">
<el-option v-for="item in orderStatus" :key="item.value" :label="item.label" :value="item.value" /> <el-option
v-for="item in orderStatus"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
<el-date-picker <el-date-picker
v-model="query.pay_time" v-model="query.pay_time"
@@ -33,13 +38,13 @@
<el-table-column label="下单时间"> <el-table-column label="下单时间">
<template #default="scope"> <template #default="scope">
<span>{{ dateFormat(scope.row["created_at"]) }}</span> <span>{{ dateFormat(scope.row['created_at']) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="支付时间"> <el-table-column label="支付时间">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row['pay_time']">{{ dateFormat(scope.row["pay_time"]) }}</span> <span v-if="scope.row['pay_time']">{{ dateFormat(scope.row['pay_time']) }}</span>
<el-tag v-else>未支付</el-tag> <el-tag v-else>未支付</el-tag>
</template> </template>
</el-table-column> </el-table-column>
@@ -74,90 +79,89 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from "vue"; import { httpGet, httpPost } from '@/utils/http'
import { httpGet, httpPost } from "@/utils/http"; import { dateFormat, removeArrayItem } from '@/utils/libs'
import { ElMessage, ElMessageBox } from "element-plus"; import { Delete, Search } from '@element-plus/icons-vue'
import { dateFormat, removeArrayItem } from "@/utils/libs"; import { ElMessage, ElMessageBox } from 'element-plus'
import { Delete, Search } from "@element-plus/icons-vue"; import { onMounted, ref } from 'vue'
// 变量定义 // 变量定义
const items = ref([]); const items = ref([])
const query = ref({ order_no: "", pay_time: [], status: -1 }); const query = ref({ order_no: '', pay_time: [], status: -1 })
const total = ref(0); const total = ref(0)
const page = ref(1); const page = ref(1)
const pageSize = ref(15); const pageSize = ref(15)
const loading = ref(true); const loading = ref(true)
const orderStatus = ref([ const orderStatus = ref([
{ value: -1, label: "全部" }, { value: -1, label: '全部' },
{ value: 0, label: "未支付" }, { value: 0, label: '未支付' },
{ value: 2, label: "已支付" }, { value: 2, label: '已支付' },
]); ])
onMounted(() => { onMounted(() => {
fetchData(); fetchData()
}); })
// 获取数据 // 获取数据
const fetchData = () => { const fetchData = () => {
query.value.page = page.value; query.value.page = page.value
query.value.page_size = pageSize.value; query.value.page_size = pageSize.value
httpPost("/api/admin/order/list", query.value) httpPost('/api/admin/order/list', query.value)
.then((res) => { .then((res) => {
if (res.data) { if (res.data) {
items.value = res.data.items; items.value = res.data.items
total.value = res.data.total; total.value = res.data.total
page.value = res.data.page; page.value = res.data.page
pageSize.value = res.data.page_size; pageSize.value = res.data.page_size
} }
loading.value = false; loading.value = false
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("获取数据失败" + e.message); ElMessage.error('获取数据失败' + e.message)
}); })
}; }
const remove = function (row) { const remove = function (row) {
httpGet("/api/admin/order/remove?id=" + row.id) httpGet('/api/admin/order/remove?id=' + row.id)
.then(() => { .then(() => {
ElMessage.success("删除成功"); ElMessage.success('删除成功')
items.value = removeArrayItem(items.value, row, (v1, v2) => { items.value = removeArrayItem(items.value, row, (v1, v2) => {
return v1.id === v2.id; return v1.id === v2.id
}); })
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("删除失败" + e.message); ElMessage.error('删除失败' + e.message)
}); })
}; }
const clearOrders = () => { const clearOrders = () => {
ElMessageBox.confirm("此操作将会删除所有未支付订单继续操作吗?", "删除提示", { ElMessageBox.confirm('此操作将会删除所有未支付订单,继续操作吗?', '删除提示', {
confirmButtonText: "确认", confirmButtonText: '确认',
cancelButtonText: "取消", cancelButtonText: '取消',
type: "warning", type: 'warning',
}).then(() => { }).then(() => {
httpGet("/api/admin/order/clear").then(() => { httpGet('/api/admin/order/clear').then(() => {
ElMessage.success("订单删除成功"); ElMessage.success('订单删除成功')
page.value = 0; page.value = 0
fetchData(); fetchData()
}); })
}); })
}; }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.order { .order {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
.handle-input { .handle-input {
max-width 150px; max-width: 150px;
margin-right 10px; margin-right: 10px;
} }
} }
.opt-box { .opt-box {
padding-bottom: 10px; padding-bottom: 10px;
display flex; display: flex;
justify-content flex-end justify-content: flex-end;
.el-icon { .el-icon {
margin-right: 5px; margin-right: 5px;
@@ -165,14 +169,13 @@ const clearOrders = () => {
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.pagination { .pagination {
margin-top: 20px; margin-top: 20px;
display flex display: flex;
justify-content right justify-content: right;
} }
} }
</style> </style>

View File

@@ -1,9 +1,27 @@
<template> <template>
<div class="container power-log" v-loading="loading"> <div class="container power-log" v-loading="loading">
<div class="handle-box"> <div class="handle-box">
<el-input v-model="query.model" placeholder="模型" class="handle-input mr10" clearable style="--el-input-height: 32px" /> <el-input
<el-input v-model="query.username" placeholder="用户名" class="handle-input mr10" clearable style="--el-input-height: 32px" /> v-model="query.model"
<el-input v-model.number="query.userid" placeholder="用户ID" class="handle-input mr10" clearable style="--el-input-height: 32px" /> placeholder="模型"
class="handle-input mr10"
clearable
style="--el-input-height: 32px"
/>
<el-input
v-model="query.username"
placeholder="用户名"
class="handle-input mr10"
clearable
style="--el-input-height: 32px"
/>
<el-input
v-model.number="query.userid"
placeholder="用户ID"
class="handle-input mr10"
clearable
style="--el-input-height: 32px"
/>
<el-select v-model="query.type" placeholder="类别" style="width: 100px"> <el-select v-model="query.type" placeholder="类别" style="width: 100px">
<el-option label="全部" :value="0" /> <el-option label="全部" :value="0" />
<el-option label="充值" :value="1" /> <el-option label="充值" :value="1" />
@@ -46,7 +64,7 @@
<el-table-column prop="balance" label="余额" /> <el-table-column prop="balance" label="余额" />
<el-table-column label="发生时间"> <el-table-column label="发生时间">
<template #default="scope"> <template #default="scope">
<span>{{ dateFormat(scope.row["created_at"]) }}</span> <span>{{ dateFormat(scope.row['created_at']) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="remark" label="备注" /> <el-table-column prop="remark" label="备注" />
@@ -70,86 +88,86 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from "vue"; import { httpPost } from '@/utils/http'
import { httpPost } from "@/utils/http"; import { dateFormat } from '@/utils/libs'
import { ElMessage } from "element-plus"; import { Search } from '@element-plus/icons-vue'
import { dateFormat } from "@/utils/libs"; import Clipboard from 'clipboard'
import { Search } from "@element-plus/icons-vue"; import { ElMessage } from 'element-plus'
import Clipboard from "clipboard"; import { onMounted, ref } from 'vue'
const items = ref([]); const items = ref([])
const total = ref(0); const total = ref(0)
const page = ref(1); const page = ref(1)
const pageSize = ref(20); const pageSize = ref(20)
const loading = ref(false); const loading = ref(false)
const query = ref({ const query = ref({
model: "", model: '',
date: [], date: [],
type: 0, type: 0,
page: 1, page: 1,
page_size: 20, page_size: 20,
}); })
const totalPower = ref(0); const totalPower = ref(0)
const tagColors = ref(["", "success", "primary", "danger", "info", "warning"]); const tagColors = ref(['', 'success', 'primary', 'danger', 'info', 'warning'])
onMounted(() => { onMounted(() => {
fetchData(); fetchData()
const clipboard = new Clipboard(".copy-order-no"); const clipboard = new Clipboard('.copy-order-no')
clipboard.on("success", () => { clipboard.on('success', () => {
ElMessage.success("复制成功"); ElMessage.success('复制成功')
}); })
clipboard.on("error", () => { clipboard.on('error', () => {
ElMessage.error("复制失败"); ElMessage.error('复制失败')
}); })
}); })
// 搜索 // 搜索
const search = () => { const search = () => {
page.value = 1; page.value = 1
fetchData(); fetchData()
}; }
// 获取数据 // 获取数据
const fetchData = () => { const fetchData = () => {
loading.value = true; loading.value = true
query.value.page = page.value; query.value.page = page.value
query.value.page_size = pageSize.value; query.value.page_size = pageSize.value
httpPost("/api/admin/powerLog/list", query.value) httpPost('/api/admin/powerLog/list', query.value)
.then((res) => { .then((res) => {
const data = res.data.data; const data = res.data.data
if (data) { if (data) {
items.value = data.items; items.value = data.items
total.value = data.total; total.value = data.total
page.value = data.page; page.value = data.page
pageSize.value = data.page_size; pageSize.value = data.page_size
} }
totalPower.value = res.data.stat; totalPower.value = res.data.stat
loading.value = false; loading.value = false
}) })
.catch((e) => { .catch((e) => {
loading.value = false; loading.value = false
ElMessage.error("获取数据失败" + e.message); ElMessage.error('获取数据失败' + e.message)
}); })
}; }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.power-log { .power-log {
.handle-box { .handle-box {
--el-input-height: 32px; --el-input-height: 32px;
margin-bottom 20px margin-bottom: 20px;
.handle-input { .handle-input {
max-width 120px; max-width: 120px;
margin-right 10px; margin-right: 10px;
} }
} }
.opt-box { .opt-box {
padding-bottom: 10px; padding-bottom: 10px;
display flex; display: flex;
justify-content flex-start justify-content: flex-start;
.el-icon { .el-icon {
margin-right: 5px; margin-right: 5px;
@@ -157,14 +175,13 @@ const fetchData = () => {
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.pagination { .pagination {
margin-top: 20px; margin-top: 20px;
display flex; display: flex;
justify-content: right; justify-content: right;
} }
} }
</style> </style>

View File

@@ -1,12 +1,11 @@
<template> <template>
<div class="container product" v-loading="loading"> <div class="container product" v-loading="loading">
<div class="handle-box"> <div class="handle-box">
<el-button type="primary" :icon="Plus" @click="add">新增</el-button> <el-button type="primary" :icon="Plus" @click="add">新增</el-button>
</div> </div>
<el-row> <el-row>
<el-table :data="items" :row-key="row => row.id" table-layout="auto"> <el-table :data="items" :row-key="(row) => row.id" table-layout="auto">
<el-table-column prop="name" label="产品名称"> <el-table-column prop="name" label="产品名称">
<template #default="scope"> <template #default="scope">
<span class="sort" :data-id="scope.row.id"> <span class="sort" :data-id="scope.row.id">
@@ -15,19 +14,19 @@
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="price" label="商品原价"/> <el-table-column prop="price" label="商品原价" />
<el-table-column prop="discount" label="优惠价"/> <el-table-column prop="discount" label="优惠价" />
<el-table-column prop="days" label="有效期()"> <el-table-column prop="days" label="有效期()">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.days === 0">长期有效</el-tag> <el-tag v-if="scope.row.days === 0">长期有效</el-tag>
<span v-else>{{ scope.row.days }}</span> <span v-else>{{ scope.row.days }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="power" label="算力"/> <el-table-column prop="power" label="算力" />
<el-table-column prop="sales" label="销量"/> <el-table-column prop="sales" label="销量" />
<el-table-column prop="enabled" label="启用状态"> <el-table-column prop="enabled" label="启用状态">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row['enabled']" @change="enable(scope.row)"/> <el-switch v-model="scope.row['enabled']" @change="enable(scope.row)" />
</template> </template>
</el-table-column> </el-table-column>
@@ -50,83 +49,81 @@
</el-table> </el-table>
</el-row> </el-row>
<el-dialog <el-dialog v-model="showDialog" :title="title" :close-on-click-modal="false">
v-model="showDialog"
:title="title"
:close-on-click-modal="false"
>
<el-form :model="item" label-width="120px" ref="formRef" :rules="rules"> <el-form :model="item" label-width="120px" ref="formRef" :rules="rules">
<el-form-item label="商品名称" prop="name"> <el-form-item label="商品名称" prop="name">
<el-input v-model="item.name" autocomplete="off"/> <el-input v-model="item.name" autocomplete="off" />
</el-form-item> </el-form-item>
<el-form-item label="商品原价" prop="price"> <el-form-item label="商品原价" prop="price">
<el-input v-model="item.price" autocomplete="off"/> <el-input v-model="item.price" autocomplete="off" />
</el-form-item> </el-form-item>
<el-form-item label="优惠价" prop="discount"> <el-form-item label="优惠价" prop="discount">
<el-input v-model="item.discount" autocomplete="off"/> <el-input v-model="item.discount" autocomplete="off" />
</el-form-item> </el-form-item>
<el-form-item label="有效期" prop="days"> <el-form-item label="有效期" prop="days">
<el-input v-model.number="item.days" autocomplete="off" placeholder="会员有效期()"/> <el-input v-model.number="item.days" autocomplete="off" placeholder="会员有效期()" />
</el-form-item> </el-form-item>
<el-form-item label="算力" prop="power"> <el-form-item label="算力" prop="power">
<el-input v-model.number="item.power" autocomplete="off" placeholder="增加算力值"/> <el-input v-model.number="item.power" autocomplete="off" placeholder="增加算力值" />
</el-form-item> </el-form-item>
<el-form-item label="启用状态" prop="enable"> <el-form-item label="启用状态" prop="enable">
<el-switch v-model="item.enabled"/> <el-switch v-model="item.enabled" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="showDialog = false">取消</el-button> <el-button @click="showDialog = false">取消</el-button>
<el-button type="primary" @click="save">提交</el-button> <el-button type="primary" @click="save">提交</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import {onMounted, reactive, ref} from "vue"; import { httpGet, httpPost } from '@/utils/http'
import {httpGet, httpPost} from "@/utils/http"; import { dateFormat, removeArrayItem } from '@/utils/libs'
import {ElMessage} from "element-plus"; import { Plus } from '@element-plus/icons-vue'
import {dateFormat, removeArrayItem} from "@/utils/libs"; import { ElMessage } from 'element-plus'
import {Plus} from "@element-plus/icons-vue"; import { Sortable } from 'sortablejs'
import {Sortable} from "sortablejs"; import { onMounted, reactive, ref } from 'vue'
// 变量定义 // 变量定义
const items = ref([]) const items = ref([])
const item = ref({}) const item = ref({})
const showDialog = ref(false) const showDialog = ref(false)
const title = ref("") const title = ref('')
const rules = reactive({ const rules = reactive({
name: [{required: true, message: '请输入产品名称', trigger: 'change',}], name: [{ required: true, message: '请输入产品名称', trigger: 'change' }],
price: [{required: true, message: '请输产品价格', trigger: 'change',}], price: [{ required: true, message: '请输产品价格', trigger: 'change' }],
discount: [{required: true, message: '请输优惠金额', trigger: 'change',}], discount: [{ required: true, message: '请输优惠金额', trigger: 'change' }],
days: [{required: true, message: '请输入有效期', trigger: 'change',}], days: [{ required: true, message: '请输入有效期', trigger: 'change' }],
}) })
const loading = ref(true) const loading = ref(true)
const formRef = ref(null) const formRef = ref(null)
// 获取数据 // 获取数据
httpGet('/api/admin/product/list').then((res) => { httpGet('/api/admin/product/list')
if (res.data) { .then((res) => {
// 初始化数据 if (res.data) {
const arr = res.data; // 初始化数据
for (let i = 0; i < arr.length; i++) { const arr = res.data
arr[i].last_used_at = dateFormat(arr[i].last_used_at) for (let i = 0; i < arr.length; i++) {
arr[i].last_used_at = dateFormat(arr[i].last_used_at)
}
items.value = arr
} }
items.value = arr loading.value = false
} })
loading.value = false .catch(() => {
}).catch(() => { ElMessage.error('获取数据失败')
ElMessage.error("获取数据失败"); })
})
onMounted(() => { onMounted(() => {
const drawBodyWrapper = document.querySelector('.el-table__body tbody') const drawBodyWrapper = document.querySelector('.el-table__body tbody')
@@ -135,12 +132,14 @@ onMounted(() => {
Sortable.create(drawBodyWrapper, { Sortable.create(drawBodyWrapper, {
sort: true, sort: true,
animation: 500, animation: 500,
onEnd({newIndex, oldIndex, from}) { onEnd({ newIndex, oldIndex, from }) {
if (oldIndex === newIndex) { if (oldIndex === newIndex) {
return return
} }
const sortedData = Array.from(from.children).map(row => row.querySelector('.sort').getAttribute('data-id')); const sortedData = Array.from(from.children).map((row) =>
row.querySelector('.sort').getAttribute('data-id')
)
const ids = [] const ids = []
const sorts = [] const sorts = []
sortedData.forEach((id, index) => { sortedData.forEach((id, index) => {
@@ -149,21 +148,21 @@ onMounted(() => {
items.value[index].sort_num = index + 1 items.value[index].sort_num = index + 1
}) })
httpPost("/api/admin/product/sort", {ids: ids, sorts: sorts}).catch(e => { httpPost('/api/admin/product/sort', { ids: ids, sorts: sorts }).catch((e) => {
ElMessage.error("排序失败" + e.message) ElMessage.error('排序失败' + e.message)
}) })
} },
}) })
}) })
const add = function () { const add = function () {
title.value = "新增产品" title.value = '新增产品'
showDialog.value = true showDialog.value = true
item.value = {} item.value = {}
} }
const edit = function (row) { const edit = function (row) {
title.value = "修改产品" title.value = '修改产品'
showDialog.value = true showDialog.value = true
item.value = row item.value = row
} }
@@ -174,15 +173,17 @@ const save = function () {
showDialog.value = false showDialog.value = false
item.value['price'] = parseFloat(item.value['price']) item.value['price'] = parseFloat(item.value['price'])
item.value['discount'] = parseFloat(item.value['discount']) item.value['discount'] = parseFloat(item.value['discount'])
httpPost('/api/admin/product/save', item.value).then((res) => { httpPost('/api/admin/product/save', item.value)
ElMessage.success('操作成功!') .then((res) => {
if (!item.value['id']) { ElMessage.success('操作成功!')
const newItem = res.data if (!item.value['id']) {
items.value.push(newItem) const newItem = res.data
} items.value.push(newItem)
}).catch((e) => { }
ElMessage.error('操作失败,' + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败,' + e.message)
})
} else { } else {
return false return false
} }
@@ -190,40 +191,44 @@ const save = function () {
} }
const enable = (row) => { const enable = (row) => {
httpPost('/api/admin/product/enable', {id: row.id, enabled: row.enabled}).then(() => { httpPost('/api/admin/product/enable', { id: row.id, enabled: row.enabled })
ElMessage.success("操作成功") .then(() => {
}).catch(e => { ElMessage.success('操作成功!')
ElMessage.error("操作失败" + e.message) })
}) .catch((e) => {
ElMessage.error('操作失败:' + e.message)
})
} }
const remove = function (row) { const remove = function (row) {
httpGet('/api/admin/product/remove?id=' + row.id).then(() => { httpGet('/api/admin/product/remove?id=' + row.id)
ElMessage.success("删除成功") .then(() => {
items.value = removeArrayItem(items.value, row, (v1, v2) => { ElMessage.success('删除成功!')
return v1.id === v2.id items.value = removeArrayItem(items.value, row, (v1, v2) => {
return v1.id === v2.id
})
})
.catch((e) => {
ElMessage.error('删除失败:' + e.message)
}) })
}).catch((e) => {
ElMessage.error("删除失败" + e.message)
})
} }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.product { .product {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
.handle-input { .handle-input {
max-width 150px; max-width: 150px;
margin-right 10px; margin-right: 10px;
} }
} }
.opt-box { .opt-box {
padding-bottom: 10px; padding-bottom: 10px;
display flex; display: flex;
justify-content flex-end justify-content: flex-end;
.el-icon { .el-icon {
margin-right: 5px; margin-right: 5px;
@@ -231,22 +236,21 @@ const remove = function (row) {
} }
.sort { .sort {
cursor move cursor: move;
.iconfont { .iconfont {
position relative position: relative;
top 1px top: 1px;
} }
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.pagination { .pagination {
padding 20px 0 padding: 20px 0;
display flex display: flex;
justify-content right justify-content: right;
} }
} }
</style> </style>

View File

@@ -2,16 +2,33 @@
<div class="container list" v-loading="loading"> <div class="container list" v-loading="loading">
<div class="handle-box"> <div class="handle-box">
<el-input v-model="query.code" placeholder="兑换码" class="handle-input mr10"></el-input> <el-input v-model="query.code" placeholder="兑换码" class="handle-input mr10"></el-input>
<el-select v-model="query.status" placeholder="核销状态" style="width: 100px" class="handle-input mr10"> <el-select
<el-option v-for="item in redeemStatus" :key="item.value" :label="item.label" :value="item.value" /> v-model="query.status"
placeholder="核销状态"
style="width: 100px"
class="handle-input mr10"
>
<el-option
v-for="item in redeemStatus"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
<el-button type="primary" :icon="Search" @click="fetchData">搜索</el-button> <el-button type="primary" :icon="Search" @click="fetchData">搜索</el-button>
<el-button type="success" :icon="Plus" @click="add">添加兑换码</el-button> <el-button type="success" :icon="Plus" @click="add">添加兑换码</el-button>
<el-button type="primary" @click="exportItems" :loading="exporting"><i class="iconfont icon-export mr-1"></i> 导出 </el-button> <el-button type="primary" @click="exportItems" :loading="exporting"
><i class="iconfont icon-export mr-1"></i> 导出
</el-button>
</div> </div>
<el-row> <el-row>
<el-table :data="items" :row-key="(row) => row.id" @selection-change="handleSelectionChange" table-layout="auto"> <el-table
:data="items"
:row-key="(row) => row.id"
@selection-change="handleSelectionChange"
table-layout="auto"
>
<el-table-column type="selection" width="38"></el-table-column> <el-table-column type="selection" width="38"></el-table-column>
<el-table-column prop="name" label="名称" /> <el-table-column prop="name" label="名称" />
<el-table-column prop="code" label="兑换码"> <el-table-column prop="code" label="兑换码">
@@ -24,7 +41,7 @@
</el-table-column> </el-table-column>
<el-table-column label="兑换人"> <el-table-column label="兑换人">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row['username'] !== ''">{{ scope.row["username"] }}</span> <span v-if="scope.row['username'] !== ''">{{ scope.row['username'] }}</span>
<el-tag v-else>未兑换</el-tag> <el-tag v-else>未兑换</el-tag>
</template> </template>
</el-table-column> </el-table-column>
@@ -32,20 +49,26 @@
<el-table-column label="生成时间"> <el-table-column label="生成时间">
<template #default="scope"> <template #default="scope">
<span>{{ dateFormat(scope.row["created_at"]) }}</span> <span>{{ dateFormat(scope.row['created_at']) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="兑换时间"> <el-table-column label="兑换时间">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row['redeemed_at'] > 0">{{ dateFormat(scope.row["redeemed_at"]) }}</span> <span v-if="scope.row['redeemed_at'] > 0">{{
dateFormat(scope.row['redeemed_at'])
}}</span>
<el-tag v-else>未兑换</el-tag> <el-tag v-else>未兑换</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="enabled" label="启用状态"> <el-table-column prop="enabled" label="启用状态">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row['enabled']" @change="set('enabled', scope.row)" :disabled="scope.row['redeemed_at'] > 0" /> <el-switch
v-model="scope.row['enabled']"
@change="set('enabled', scope.row)"
:disabled="scope.row['redeemed_at'] > 0"
/>
</template> </template>
</el-table-column> </el-table-column>
@@ -102,160 +125,160 @@
</template> </template>
<script setup> <script setup>
import { onMounted, onUnmounted, ref } from "vue"; import { showMessageError } from '@/utils/dialog'
import { httpGet, httpPost, httpPostDownload } from "@/utils/http"; import { httpGet, httpPost, httpPostDownload } from '@/utils/http'
import { ElMessage } from "element-plus"; import { dateFormat, substr, UUID } from '@/utils/libs'
import { dateFormat, substr, UUID } from "@/utils/libs"; import { DocumentCopy, Plus, Search } from '@element-plus/icons-vue'
import { DocumentCopy, Plus, Search } from "@element-plus/icons-vue"; import ClipboardJS from 'clipboard'
import { showMessageError } from "@/utils/dialog"; import { ElMessage } from 'element-plus'
import ClipboardJS from "clipboard"; import { onMounted, onUnmounted, ref } from 'vue'
// 变量定义 // 变量定义
const items = ref([]); const items = ref([])
const loading = ref(true); const loading = ref(true)
const query = ref({ code: "", status: -1 }); const query = ref({ code: '', status: -1 })
const redeemStatus = ref([ const redeemStatus = ref([
{ value: -1, label: "全部" }, { value: -1, label: '全部' },
{ value: 0, label: "未核销" }, { value: 0, label: '未核销' },
{ value: 1, label: "已核销" }, { value: 1, label: '已核销' },
]); ])
const showDialog = ref(false); const showDialog = ref(false)
const dialogLoading = ref(false); const dialogLoading = ref(false)
const item = ref({ name: "", power: 0, num: 1 }); const item = ref({ name: '', power: 0, num: 1 })
const itemIds = ref([]); const itemIds = ref([])
const exporting = ref(false); const exporting = ref(false)
const clipboard = ref(null); const clipboard = ref(null)
onMounted(() => { onMounted(() => {
clipboard.value = new ClipboardJS(".copy-code"); clipboard.value = new ClipboardJS('.copy-code')
clipboard.value.on("success", () => { clipboard.value.on('success', () => {
ElMessage.success("复制成功"); ElMessage.success('复制成功')
}); })
clipboard.value.on("error", () => { clipboard.value.on('error', () => {
ElMessage.error("复制失败"); ElMessage.error('复制失败')
}); })
fetchData(); fetchData()
}); })
onUnmounted(() => { onUnmounted(() => {
clipboard.value.destroy(); clipboard.value.destroy()
}); })
const add = () => { const add = () => {
item.value = { name: "100算力点卡", power: 100, num: 1 }; item.value = { name: '100算力点卡', power: 100, num: 1 }
showDialog.value = true; showDialog.value = true
dialogLoading.value = false; dialogLoading.value = false
}; }
const save = () => { const save = () => {
if (item.value.name === "") { if (item.value.name === '') {
return showMessageError("请输入兑换码名称"); return showMessageError('请输入兑换码名称')
} }
if (item.value.power === 0) { if (item.value.power === 0) {
return showMessageError("请输入算力额度"); return showMessageError('请输入算力额度')
} }
if (item.value.num <= 0) { if (item.value.num <= 0) {
return showMessageError("请输入生成数量"); return showMessageError('请输入生成数量')
} }
dialogLoading.value = true; dialogLoading.value = true
httpPost("/api/admin/redeem/create", item.value) httpPost('/api/admin/redeem/create', item.value)
.then((res) => { .then((res) => {
ElMessage.success(`成功生成了${res.data.counter}个兑换码`); ElMessage.success(`成功生成了${res.data.counter}个兑换码`)
showDialog.value = false; showDialog.value = false
fetchData(); fetchData()
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("生成失败" + e.message); ElMessage.error('生成失败' + e.message)
}); })
}; }
const set = (filed, row) => { const set = (filed, row) => {
httpPost("/api/admin/redeem/set", { id: row.id, filed: filed, value: row[filed] }) httpPost('/api/admin/redeem/set', { id: row.id, filed: filed, value: row[filed] })
.then(() => { .then(() => {
ElMessage.success("操作成功"); ElMessage.success('操作成功')
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("操作失败" + e.message); ElMessage.error('操作失败' + e.message)
}); })
}; }
const page = ref(1); const page = ref(1)
const pageSize = ref(12); const pageSize = ref(12)
const total = ref(0); const total = ref(0)
const fetchData = () => { const fetchData = () => {
query.value.page = page.value; query.value.page = page.value
query.value.page_size = pageSize.value; query.value.page_size = pageSize.value
httpGet("/api/admin/redeem/list", query.value) httpGet('/api/admin/redeem/list', query.value)
.then((res) => { .then((res) => {
if (res.data) { if (res.data) {
items.value = res.data.items; items.value = res.data.items
total.value = res.data.total; total.value = res.data.total
page.value = res.data.page; page.value = res.data.page
pageSize.value = res.data.page_size; pageSize.value = res.data.page_size
} }
loading.value = false; loading.value = false
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("获取数据失败" + e.message); ElMessage.error('获取数据失败' + e.message)
}); })
}; }
const remove = function (row) { const remove = function (row) {
httpGet("/api/admin/redeem/remove?id=" + row.id) httpGet('/api/admin/redeem/remove?id=' + row.id)
.then(() => { .then(() => {
ElMessage.success("删除成功"); ElMessage.success('删除成功')
fetchData(); fetchData()
}) })
.catch((e) => { .catch((e) => {
ElMessage.error("删除失败" + e.message); ElMessage.error('删除失败' + e.message)
}); })
}; }
const handleSelectionChange = (items) => { const handleSelectionChange = (items) => {
itemIds.value = items.map((item) => item.id); itemIds.value = items.map((item) => item.id)
}; }
const exportItems = () => { const exportItems = () => {
query.value.ids = itemIds.value; query.value.ids = itemIds.value
exporting.value = true; exporting.value = true
httpPostDownload("/api/admin/redeem/export", query.value) httpPostDownload('/api/admin/redeem/export', query.value)
.then((response) => { .then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data])); const url = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement("a"); const link = document.createElement('a')
link.href = url; link.href = url
link.setAttribute("download", UUID() + ".csv"); // 设置下载文件的名称 link.setAttribute('download', UUID() + '.csv') // 设置下载文件的名称
document.body.appendChild(link); document.body.appendChild(link)
link.click(); link.click()
// 移除 <a> 标签 // 移除 <a> 标签
document.body.removeChild(link); document.body.removeChild(link)
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url)
exporting.value = false; exporting.value = false
}) })
.catch(() => { .catch(() => {
exporting.value = false; exporting.value = false
showMessageError("下载失败"); showMessageError('下载失败')
}); })
}; }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
.list { .list {
.handle-box { .handle-box {
margin-bottom 20px margin-bottom: 20px;
.handle-input { .handle-input {
max-width 150px; max-width: 150px;
margin-right 10px; margin-right: 10px;
} }
} }
.opt-box { .opt-box {
padding-bottom: 10px; padding-bottom: 10px;
display flex; display: flex;
justify-content flex-end justify-content: flex-end;
.el-icon { .el-icon {
margin-right: 5px; margin-right: 5px;
@@ -263,22 +286,21 @@ const exportItems = () => {
} }
.el-select { .el-select {
width: 100% width: 100%;
} }
.copy-code { .copy-code {
cursor pointer cursor: pointer;
margin-left 6px margin-left: 6px;
position relative position: relative;
top 2px top: 2px;
font-size 14px font-size: 14px;
} }
.pagination { .pagination {
margin-top: 20px; margin-top: 20px;
display flex; display: flex;
justify-content: right; justify-content: right;
} }
} }
</style> </style>

View File

@@ -842,17 +842,17 @@ const onUploadImg = (files, callback) => {
// } // }
</script> </script>
<style lang="stylus" scoped> <style lang="scss" scoped>
@use '../../assets/css/admin/form.scss' as *; @use '../../assets/css/admin/form.scss' as *;
@use '../../assets/css/main.scss' as *; @use '../../assets/css/main.scss' as *;
.system-config { .system-config {
display flex display: flex;
justify-content center justify-content: center;
.sys-tabs { .sys-tabs {
width 100% width: 100%;
background-color var(--el-bg-color) background-color: var(--el-bg-color);
padding 10px 20px 40px 20px padding: 10px 20px 40px 20px;
//border: 1px solid var(--el-border-color); //border: 1px solid var(--el-border-color);
} }
} }