Compare commits

...

10 Commits

Author SHA1 Message Date
421300db39 调整 2024-12-18 14:37:54 +08:00
016a01ba8d 调整 2024-12-18 14:30:37 +08:00
76b4122f1c 整合业务模块 2024-12-16 22:04:05 +08:00
夕阳微笑1
2f110d5e8d 角色管理的修改 2024-12-16 13:45:55 +08:00
夕阳微笑1
430553412c 登录页以及个人中心修改 2024-12-16 13:45:03 +08:00
夕阳微笑1
ca02a903ce 图片管理样式修改 2024-12-16 13:44:38 +08:00
夕阳微笑1
91f4cd1487 标签管理修改 2024-12-16 13:44:16 +08:00
夕阳微笑1
3ad716ba12 轮播图样式修改 2024-12-16 13:44:01 +08:00
夕阳微笑1
027772c637 默认头像更改 2024-12-16 13:43:49 +08:00
夕阳微笑1
c206a82e52 登录页和首页更改 2024-12-16 13:43:07 +08:00
32 changed files with 1444 additions and 260 deletions

View File

@ -6,11 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="keywords"
content="vue-next-adminvue-prev-adminvue-admin-wonderful后台管理系统一站式平台模板希望可以帮你完成快速开发。vue2.xvue2.0vue2vue3vue3.xvue3.0CompositionAPItypescriptelement pluselementplusadminwonderfulwonderful-nextvue-next-adminvitevite-admin快速,高效,后台模板,后台系统,管理系统"
content="pixel-admin是系统管理pixel.ai展示平台的后台管理系统。vue3vue3.xvue3.0CompositionAPItypescriptelement-pluselementplusadminwonderfulwonderful-nextvite快速高效后台系统管理系统"
/>
<meta
name="description"
content="vue-next-admin基于 vue3 + CompositionAPI + typescript + vite + element plus适配手机、平板、pc 的后台开源免费管理系统模板vue-prev-admin基于 vue2 + element ui适配手机、平板、pc 的后台开源免费管理系统模板"
content="pixel-admin是基于 vue3 + CompositionAPI + typescript + vite + element-plus适配手机、平板、pc 的ai制图后台管理系统"
/>
<link rel="icon" href="/favicon.ico" />
<title>vue-next-admin</title>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -20,7 +20,7 @@ import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
import setIntroduction from '/@/utils/setIconfont';
import logoMini from '/@/assets/logo-mini.svg';
import logoMini from '/@/assets/logo.png';
//
const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
@ -74,7 +74,7 @@ const getGlobalI18n = computed(() => {
let link: any = document.querySelector("link[rel*='icon']") || document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
link.href = themeConfig.value.logoMini||logoMini;
link.href = themeConfig.logoMini||logoMini;
document.getElementsByTagName("head")[0].appendChild(link);
//

View File

@ -1,2 +1,3 @@
export const baseUrlHost = "/vueAdminApi";
// export const baseUrlHost = "/vueAdminApi";
export const baseUrlHost = "/pixelApi";
export const pixelUrlHost = "/pixelApi";

View File

@ -1,4 +1,5 @@
import axios from "axios";
import { ElMessage } from "element-plus";
const downloadPhoto = (imgSrc:any) => {
axios({
@ -15,8 +16,9 @@ const downloadPhoto = (imgSrc:any) => {
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
ElMessage({message: '图片下载成功!',type: 'success',});
}).catch(error => {
console.error('Download error:', error);
ElMessage({message: '图片下载失败!',type: 'error',});
});
};

View File

@ -25,5 +25,12 @@ export function messageApi() {
method: 'get',
});
},
updateMessage: (data: object) => {
return request({
url: baseUrlHost + '/cpMessage',
method: 'put',
data,
})
},
};
}

View File

@ -0,0 +1,50 @@
import request from '/@/utils/request';
import { pixelUrlHost } from '/@/api/baseUrlHost';
/**
* request.post(xxx) post params data
* get请求时paramsdata
*
* api接口集合
* @method getServicesList
* @method getApplyDetail
* @method updateApply
*/
export function applyApi() {
return {
getServicesList: (params: object) => {
return request({
url: pixelUrlHost + '/paServices',
method: 'get',
params,
});
},
getApplyDetail: (id: Number) => {
return request({
url: pixelUrlHost + `/paServices/${id}`,
method: 'get',
});
},
saveApply: (data: object) => {
return request({
url: pixelUrlHost + '/paServices',
method: 'post',
data,
})
},
updateApply: (data: object) => {
return request({
url: pixelUrlHost + '/paServices',
method: 'put',
data,
})
},
deleteApply: (id: Number) => {
return request({
url: pixelUrlHost + `/paServices/${id}`,
method: 'delete',
});
},
};
}

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -11,7 +11,7 @@ export default {
UserManage: 'UserManage',
systemDic: 'systemDic',
article: 'article',
apply: 'apply',
applyDetail: 'applyDetail',
message: 'message',
order: 'order',
articleContent: 'articleContent',
@ -100,6 +100,16 @@ export default {
layoutLinkView: 'LinkView',
layoutIframeViewOne: 'IframeViewOne',
layoutIframeViewTwo: 'IframeViewTwo',
/**
* start
* @name
*/
apply: 'apply',
/**
* end
*/
},
staticRoutes: {
signIn: 'signIn',

View File

@ -11,7 +11,7 @@ export default {
systemDept: '部门管理',
systemDic: '字典管理',
article: '文章管理',
apply: '应用管理',
applyDetail: '应用服务详情',
carousel: '轮播图管理',
clients: '客户管理',
photos: '图片管理',
@ -103,6 +103,16 @@ export default {
layoutLinkView: '外链',
layoutIframeViewOne: '内嵌 iframe1',
layoutIframeViewTwo: '内嵌 iframe2',
/**
* start
* @name
*/
apply: '应用管理',
/**
* end
*/
},
staticRoutes: {
signIn: '登录',

View File

@ -11,7 +11,7 @@ export default {
systemDept: '部門管理',
systemDic: '字典管理',
article: '文章管理',
apply: '应用管理',
applyDetail: '應用服務詳情',
message: '聯言管理',
articleContent: '文章詳情',
order: '訂單管理',
@ -100,6 +100,16 @@ export default {
layoutLinkView: '外鏈',
layoutIframeViewOne: '内嵌 iframe1',
layoutIframeViewTwo: '内嵌 iframe2',
/**
* start
* @name
*/
apply: '應用管理',
/**
* end
*/
},
staticRoutes: {
signIn: '登入',

View File

@ -12,7 +12,7 @@
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import logoMini from '/@/assets/logo-mini.svg';
import logoMini from '/@/assets/logo.png';
//
const storesThemeConfig = useThemeConfig();

View File

@ -1,6 +1,6 @@
<template>
<div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
<!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
<div class="layout-navbars-breadcrumb-user-icon">
<i class="iconfont icon-ziti" :title="$t('message.user.title0')"></i>
</div>
@ -11,8 +11,8 @@
<el-dropdown-item command="small" :disabled="state.disabledSize === 'small'">{{ $t('message.user.dropdownSmall') }}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
</el-dropdown> -->
<!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
<div class="layout-navbars-breadcrumb-user-icon">
<i
class="iconfont"
@ -27,22 +27,22 @@
<el-dropdown-item command="zh-tw" :disabled="state.disabledI18n === 'zh-tw'">繁體中文</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
</el-dropdown> -->
<!-- <div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
<el-icon :title="$t('message.user.title2')">
<ele-Search />
</el-icon>
</div>
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
</div> -->
<!-- <div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
<i class="icon-skin iconfont" :title="$t('message.user.title3')"></i>
</div>
<div class="layout-navbars-breadcrumb-user-icon" ref="userNewsBadgeRef" v-click-outside="onUserNewsClick">
</div> -->
<!-- <div class="layout-navbars-breadcrumb-user-icon" ref="userNewsBadgeRef" v-click-outside="onUserNewsClick">
<el-badge :is-dot="true">
<el-icon :title="$t('message.user.title4')">
<ele-Bell />
</el-icon>
</el-badge>
</div>
</div> -->
<el-popover
ref="userNewsRef"
:virtual-ref="userNewsBadgeRef"

View File

@ -251,11 +251,18 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
}
]
},
/**
*
* start
* @name
* /apply
*
*/
// 应用管理
// {
// path: '/apply',
// name: 'apply',
// component: () => import('/@/views/apply/index.vue'),
// component: () => import('/@/views/service/apply/index.vue'),
// meta: {
// title: 'message.router.apply',
// isLink: '',
@ -266,8 +273,29 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
// roles: ['admin', 'common'],
// icon: 'iconfont icon-crew_feature',
// },
// children: []
// children: [
// {
// path: '/apply/detail',
// name: 'applyDetail',
// component: () => import('/@/views/service/apply/detail.vue'),
// meta: {
// title: 'message.router.applyDetail',
// isLink: '',
// isHide: true,
// isKeepAlive: true,
// isAffix: false,
// isIframe: false,
// roles: ['admin', 'common'],
// icon: 'ele-Document',
// },
// },
// ]
// },
/**
* end
*/
// 轮播图管理
{
path: '/carousel',

View File

@ -78,7 +78,7 @@ export const useThemeConfig = defineStore('themeConfig', {
*
*/
// 是否开启侧边栏 Logo
isShowLogo: false,
isShowLogo: true,
// 初始化变量,用于 el-scrollbar 的高度更新,请勿删除
isShowLogoChange: false,
// 是否开启 Breadcrumb强制经典、横向布局不显示
@ -102,13 +102,13 @@ export const useThemeConfig = defineStore('themeConfig', {
// 是否开启色弱模式
isInvert: false,
// 是否开启水印
isWartermark: true,
isWartermark: false,
// 水印文案
wartermarkText: 'vue-next-admin',
wartermarkText: 'pixel.ai制图后台',
// 页脚版权信息内容
globalFootTitleMsg: '深圳市 xxx 公司版权所有',
globalFootTitleMsg: '广州市 xxx 公司版权所有',
// 页脚tip
globalFootTipMsg: 'vue-next-adminMade by lyt with ❤️',
globalFootTipMsg: 'ai创作专享您的制图~~~',
/**
*
@ -141,17 +141,17 @@ export const useThemeConfig = defineStore('themeConfig', {
* /
*/
// 网站主标题(菜单导航、浏览器当前网页标题)
globalTitle: 'vue-next-admin',
globalTitle: 'pixel.ai制图后台',
// 网站副标题(登录页顶部文字)
globalViceTitle: 'vueNextAdmin',
globalViceTitle: 'pixel.ai',
// 网站副标题(登录页顶部文字)
globalViceTitleMsg: '专注、免费、开源、维护、解疑',
globalViceTitleMsg: '多种类、多趣味、多质图',
// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
globalI18n: 'zh-cn',
// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'
globalComponentSize: 'large',
// 网站logo
logoMini: '/@/assets/logo-mini.svg',
logoMini: '/@/assets/logo.png',
},
}),
actions: {

View File

@ -1,6 +1,8 @@
import { defineStore } from 'pinia';
import Cookies from 'js-cookie';
import { Session } from '/@/utils/storage';
import admin from '/@/assets/图片2.png';
import user from '/@/assets/头像.png';
/**
*
@ -52,13 +54,16 @@ export const useUserInfo = defineStore('userInfo', {
defaultRoles = testRoles;
defaultAuthBtnList = testAuthBtnList;
}
// 图片基本路径
// const viteUrl = import.meta.env.VITE_API_URL;
// 用户信息模拟数据
const userInfos = {
userName: userName,
photo:
userName === 'admin'
? 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500'
: 'https://img2.baidu.com/it/u=2370931438,70387529&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
? admin
: user,
time: new Date().getTime(),
roles: defaultRoles,
authBtnList: defaultAuthBtnList,

View File

@ -45,6 +45,9 @@ service.interceptors.response.use(
.then(() => {})
.catch(() => {});
}
// else if(res.code === 502 && res.errorMsg){
// return res;
// }
return Promise.reject(service.interceptors.response);
} else {
return res;

View File

@ -1,7 +1,7 @@
<template>
<div class="system-dept-container layout-padding">
<el-card shadow="hover" class="layout-padding-auto">
<div class="system-dept-search mb15">
<!-- <div class="system-dept-search mb15">
<span>名称:</span>
<el-input size="default" placeholder="请输入" v-model="state.tableData.param.name" class="ml10" style="max-width: 250px;margin-right: 20px;"> </el-input>
<el-button @click="getTableData" size="default" type="primary" class="ml10" style="margin-right: 15px;">
@ -15,30 +15,52 @@
<ele-RefreshRight />
</el-icon>
重置
</el-button>
<el-button @click="switchDrawer(0)" size="default" type="primary" class="ml10">
</el-button> -->
<!-- <el-button @click="switchDrawer(0)" size="default" type="primary" class="ml10">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增轮播图
</el-button>
</div>
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%">
<el-table-column prop="name" label="轮播图名称" show-overflow-tooltip width="350px"></el-table-column>
<el-table-column label="跳转链接" show-overflow-tooltip width="350px" align="center">
</el-button> -->
<!-- </div> -->
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%;" class="custom-table">
<el-table-column prop="name" label="轮播图名称" width="150px" show-overflow-tooltip></el-table-column>
<el-table-column label="素材" align="center">
<template #default="scope">
<a :href="jpgFormatter(scope.row)">{{ jpgFormatter(scope.row) }}</a>
<el-image :src="encodeURI(viteUrl+scope.row.path)" lazy
preview-teleported="true" :preview-src-list="[encodeURI(viteUrl+scope.row.path)]"/>
<!-- <a :href="jpgFormatter(scope.row)">{{ jpgFormatter(scope.row) }}</a> -->
<!-- <el-button @click="jpgJump(scope.row)" text type="primary">{{ jpgFormatter(scope.row) }}</el-button> -->
</template>
</el-table-column>
<el-table-column prop="orderNum" label="排序号" sortable show-overflow-tooltip width="350px" align="center"></el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip width="350px" align="center"></el-table-column>
<el-table-column label="跳转地址" show-overflow-tooltip align="center">
<template #default="scope">
<!-- <a :href="jpgFormatter(scope.row)">{{ jpgFormatter(scope.row) }}</a> -->
<el-button @click="jpgJump(scope.row)" text type="primary">{{ scope.row.url }}</el-button>
</template>
</el-table-column>
<el-table-column prop="orderNum" label="排序号" sortable show-overflow-tooltip align="center"></el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip align="center"></el-table-column>
<el-table-column label="操作" width="300" align="center">
<template #default="scope">
<el-button size="small" text type="primary" @click="switchDrawer(scope.row.id,1)">编辑</el-button>
<el-button size="small" text type="primary" @click="deleteBanner(scope.row.id)">删除</el-button>
<el-button size="small" text type="primary" @click="switchDrawer(scope.row.id,1)">
<el-icon>
<ele-EditPen/>
</el-icon>
</el-button>
<el-button size="small" text type="primary" @click="deleteBanner(scope.row.id)">
<el-icon>
<ele-Delete/>
</el-icon>
</el-button>
</template>
</el-table-column>
</el-table>
<div class="add-banner" @click="switchDrawer(0)">
+ 添加banner
</div>
<!-- 新增或编辑弹窗 -->
<el-drawer
v-model="dialog"
@ -69,7 +91,7 @@
<el-icon><Plus /></el-icon>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<img class="el-upload-list__item-thumbnail" :src="encodeURI(file.url)" alt="" />
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@ -92,8 +114,8 @@
<img :src="dialogImageUrl" style="width: 100%" alt="Preview Image" />
</el-dialog>
</el-form-item>
<el-form-item label="跳转链接:" prop="path">
<el-input v-model="state.formData.path" />
<el-form-item label="跳转链接:" prop="url">
<el-input v-model="state.formData.url" />
</el-form-item>
<el-form-item label="排序号:" prop="orderNum">
<el-input v-model.number="state.formData.orderNum" />
@ -108,7 +130,7 @@
</div>
</el-drawer>
<!-- 分页器 -->
<el-pagination
<!-- <el-pagination
@size-change="onHandleSizeChange"
@current-change="onHandleCurrentChange"
class="mt15"
@ -120,7 +142,7 @@
layout="total, sizes, prev, pager, next, jumper"
:total="state.tableData.total"
>
</el-pagination>
</el-pagination> -->
</el-card>
</div>
</template>
@ -152,6 +174,7 @@ const state = reactive({
fileList: [],
name:"",
path:"",
url: '',
orderNum: "",
loading: false
},
@ -193,35 +216,36 @@ const getTableData = async() => {
};
//
const reset = () =>{
state.tableData.param = {
name: '',
current: 1,
size: 10,
}
getTableData();
}
// const reset = () =>{
// state.tableData.param = {
// name: '',
// current: 1,
// size: 10,
// }
// getTableData();
// }
//
const viteUrl = import.meta.env.VITE_API_URL;
//
const jpgFormatter = (row: any) => {
console.log("44444444444",row);
let newPath;
if (row?.data) {
newPath = row.data.path.replaceAll('\\', '/')
console.log("11111111111",newPath);
} else {
newPath = row.path.replaceAll('\\\\', '/')
console.log("333333333333",newPath);
}
newPath = newPath.includes('http://localhost:8888/') ? newPath : viteUrl + newPath
console.log("22222222222222222",newPath);
newPath = newPath.includes('http://localhost:8888/') ? newPath : encodeURI(viteUrl + newPath)
return `${newPath}`;
}
};
//
const jpgJump = (row:any) =>{
window.open(row.url);
};
//
const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
@ -230,16 +254,16 @@ const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
}
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.size = val;
getTableData();
};
// const onHandleSizeChange = (val: number) => {
// state.tableData.param.size = val;
// getTableData();
// };
//
const onHandleCurrentChange = (val: number) => {
state.tableData.param.current = val;
getTableData();
};
// //
// const onHandleCurrentChange = (val: number) => {
// state.tableData.param.current = val;
// getTableData();
// };
// #endregion
// #region
@ -285,10 +309,7 @@ const disabled = ref(false)
const handleChange: UploadProps['onChange'] = async (file: uploadFile, uploadFiles) => {
state.formData.loading = true;
if (file.raw.type === 'image/png' || file.raw.type === 'image/jpeg' || file.raw.type === 'image/jpg') {
console.log("handleChange1",file);
console.log("handleChange2",uploadFiles);
state.formData.fileList = uploadFiles;
console.log("handleChange",state.formData.fileList);
uploadDisabled.value = true;
try {
const formdata = new FormData();
@ -297,8 +318,8 @@ const handleChange: UploadProps['onChange'] = async (file: uploadFile, uploadFil
console.log("result", res);
console.log("state.formData", state.formData);
if(res?.success){
state.formData.url = jpgFormatter(res);
state.formData.path = jpgFormatter(res);
// state.formData.url = jpgFormatter(res);
state.formData.path = res.data.path;
ElMessage({
message: '图片上传成功!',
type: 'success',
@ -365,10 +386,6 @@ const deleteBanner = (id: number) => {
state.tableData.loading = false;
}
}).catch(() => {
ElMessage({
type: 'info',
message: '删除取消',
})
})
}
@ -454,17 +471,73 @@ onMounted(() => {
</script>
<style scoped lang="scss">
.system-dept-container {
:deep(.el-card__body) {
display: flex;
flex-direction: column;
flex: 1;
overflow: auto;
.el-table {
flex: 1;
}
}
.add-banner {
width: 100%; //
margin: 16px 0;
text-align: center;
color: #409eff;
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
height: 100px; //
border: 2px dashed #dcdfe6;
border-radius: 8px;
}
.system-dept-container {
:deep(.el-card__body) {
display: flex;
flex-direction: column;
flex: 1;
overflow-y: auto;
max-height: 1000px; //
padding: 20px;
.el-table {
flex: 1;
width: 100%;
table-layout: fixed; //
}
// .el-table th, .el-table td {
// text-align: center;
// padding: 12px 8px;
// word-break: break-all;
// }
}
};
::v-deep .el-image {
width: 250px; //
height: 150px; //
object-fit: cover; //
display: block;
}
img[alt="加载失败"] {
content: url('path_to_placeholder_image.png'); //
width: 250px; //
height: 100px;
}
::v-deep .el-table__body{
width: 100% !important;
}
::v-deep .el-table__body-wrapper .el-table__row {
margin-bottom: 10px; //
border: 1px solid #dcdfe6; // 使
border-radius: 8px; //
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); //
background-color: #fff;
overflow: hidden; //
}
::v-deep .el-drawer .el-drawer__header {
margin: 15px 0 20px 0!important;
border-bottom: 0!important;

View File

@ -18,7 +18,24 @@
添加标签
</el-button>
</div>
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%" :header-cell-style="{ backgroundColor: '#fafafa', height: '40px' }">
<!-- <el-tree
:data="treeData"
:props="defaultProps"
node-key="id"
default-expand-all
:highlight-current="true"
@node-click="handleNodeClick"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ data.name }}</span>
<span>
<el-button size="mini" type="text" @click="toShowDetail(data.id)">查看详情</el-button>
<el-button size="mini" type="text" @click="toEditArticle(data.id)">编辑</el-button>
<el-button size="mini" type="text" @click="deleteArticle(data.id)">删除</el-button>
</span>
</span>
</el-tree> -->
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%" :header-cell-style="{ backgroundColor: '#fafafa', height: '40px' }">
<el-table-column prop="moduleName" label="模块名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="parentName" label="父标签" show-overflow-tooltip></el-table-column>
<el-table-column prop="name" label="标签名称" show-overflow-tooltip></el-table-column>
@ -72,6 +89,11 @@ const addLabelDialogRef = ref();
const editLabelDialog = defineAsyncComponent(() => import('/@/views/label/editDialog.vue'));
const editLabelDialogRef = ref();
interface Tree {
label: string
children?: Tree[]
}
//
const state = reactive({
tableData: {
@ -93,6 +115,13 @@ const moduleList = ref([]);
//
const labelList = ref([]);
//
const treeData = ref([]);
const defaultProps = {
children: 'children',
label: 'name'
};
// api
const labelapi = labelApi();
@ -119,6 +148,7 @@ const getTableData = async() => {
if(res?.success){
state.tableData.data = res.data.records;
state.tableData.total = res.data.total;
buildTreeData();
}else{
ElMessage.error('标签列表获取失败!');
}
@ -128,6 +158,27 @@ const getTableData = async() => {
}
};
const buildTreeData = () => {
const moduleMap = {};
//
state.tableData.data.forEach(item => {
if (!moduleMap[item.moduleName]) {
moduleMap[item.moduleName] = {
name: item.moduleName,
children: []
};
}
moduleMap[item.moduleName].children.push({ ...item });
});
treeData.value = Object.values(moduleMap);
console.log(treeData.value); //
};
const handleNodeClick = (data: Tree) => {
console.log(data)
}
//
const handleChangeModule = async(val: any) => {
@ -147,24 +198,6 @@ const handleChangeModule = async(val: any) => {
}
};
// //
// const handleChangeModule = async(val) => {
// try {
// if (!val) {
// labelList.value = [];
// state.tableData.data = [];
// state.tableData.total = 0;
// return;
// }
// const op = moduleList.value.find(item => item.moduleName === val);
// if (op) {
// state.tableData.param.moduleid = op.id;
// await getTableData();
// }
// } catch (error) {
// ElMessage.error('');
// }
// }
//
const reset = (index = 0) =>{
@ -233,13 +266,13 @@ const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
// return row.articletype === 1 ? '' : '';
// }
//
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.size = val;
getTableData();
};
//
//
const onHandleCurrentChange = (val: number) => {
state.tableData.param.current = val;
getTableData();
@ -250,76 +283,19 @@ onMounted(() => {
getTableData();
});
// const totalData = ref([]); //
// //
// const getModuleList = async () => {
// try {
// let res = await labelapi.getModuleList();
// if (res?.success) {
// moduleList.value = res.data;
// await getAllData(); //
// } else {
// ElMessage.error('');
// }
// } catch (error) {
// ElMessage.error('');
// }
// };
// //
// const getAllData = async () => {
// try {
// const requests = moduleList.value.map(module => labelapi.getLabelList({ moduleid: module.id, current: 1, size: 10 })); //
// const results = await Promise.all(requests);
// totalData.value = results
// .filter(res => res?.success) //
// .flatMap(res => res.data.records || []); //
// state.tableData.total = totalData.value.length;
// updatePageData(); //
// } catch (error) {
// ElMessage.error('');
// } finally {
// state.tableData.loading = false;
// }
// };
// //
// const updatePageData = () => {
// const start = (state.tableData.param.current - 1) * state.tableData.param.size;
// const end = start + state.tableData.param.size;
// state.tableData.data = totalData.value.slice(start, end);
// };
// //
// const onHandleSizeChange = (val) => {
// state.tableData.param.size = val;
// updatePageData(); //
// };
// const onHandleCurrentChange = (val) => {
// state.tableData.param.current = val;
// updatePageData(); //
// };
// onMounted(() => {
// getModuleList();
// });
</script>
<style scoped lang="scss">
.system-dept-container {
:deep(.el-card__body) {
display: flex;
flex-direction: column;
flex: 1;
overflow: auto;
.el-table {
:deep(.el-card__body) {
display: flex;
flex-direction: column;
flex: 1;
overflow: auto;
.el-tree {
flex: 1;
}
}
}
}
</style>

View File

@ -119,7 +119,7 @@ const onSignIn = async () => {
signInSuccess(isNoPower);
}
}else{
ElMessage.error(res.message);
ElMessage.error(res.errorMsg||res.message);
}
} catch (error) {
ElMessage.error('账号或密码错误!');

View File

@ -4,7 +4,7 @@
<div class="login-left-logo">
<img :src="logoMini" />
<div class="login-left-logo-text">
<span>{{ getThemeConfig.globalViceTitle }}</span>
<span>{{ getThemeConfig.globalTitle }}</span>
<span class="login-left-logo-text-msg">{{ getThemeConfig.globalViceTitleMsg }}</span>
</div>
</div>
@ -18,7 +18,7 @@
<span class="login-right-warp-one"></span>
<span class="login-right-warp-two"></span>
<div class="login-right-warp-mian">
<div class="login-right-warp-main-title">{{ getThemeConfig.globalTitle }} 欢迎您</div>
<div class="login-right-warp-main-title">{{ getThemeConfig.globalTitle }}</div>
<div class="login-right-warp-main-form">
<div v-if="!state.isScan">
<el-tabs v-model="state.tabsActiveName">
@ -30,11 +30,11 @@
</el-tab-pane>
</el-tabs>
</div>
<Scan v-if="state.isScan" />
<!-- <Scan v-if="state.isScan" />
<div class="login-content-main-sacn" @click="state.isScan = !state.isScan">
<i class="iconfont" :class="state.isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
<div class="login-content-main-sacn-delta"></div>
</div>
</div> -->
</div>
</div>
</div>
@ -47,7 +47,7 @@ import { defineAsyncComponent, onMounted, reactive, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
import logoMini from '/@/assets/logo-mini.svg';
import logoMini from '/@/assets/logo.png';
import loginMain from '/@/assets/login-main.svg';
import loginBg from '/@/assets/login-bg.svg';

View File

@ -16,6 +16,9 @@
</el-form-item>
<el-form-item label="留言:">
{{ state.ruleForm.message }}
</el-form-item>
<el-form-item label="回复:" v-if="state.ruleForm.replay">
{{ state.ruleForm.replay }}
</el-form-item>
</el-form>
<template #footer>
@ -41,7 +44,8 @@ const state = reactive({
phone: "",
type: "",
message: "",
createtime: []
createtime: [],
replay: null
},
dialog: {
isShowDialog: false,

View File

@ -28,6 +28,7 @@
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="toShowDetail(scope.row.id)">查看详情</el-button>
<el-button size="small" text type="primary" @click="replay(scope.row)">回复</el-button>
</template>
</el-table-column>
</el-table>
@ -46,6 +47,24 @@
</el-pagination>
</el-card>
<MessageDialog ref="messageDialogRef" />
<!-- 回复弹窗 -->
<el-dialog style="height: 300px;" :title="state.replayDialog.title" v-model="state.replayDialog.show" width="400px">
<div class="dialog-content">
<el-form ref="replayFormRef" v-loading="state.replayDialog.loading" label-width="80px" :model="state.replayDialog.form" :rules="state.replayDialog.rules">
<el-form-item label="留言:">
{{ state.replayDialog.message }}
</el-form-item>
<el-form-item label="回复:" label-width="80px" style="margin-top: 20px;flex: 1;" prop="replay">
<el-input v-model="state.replayDialog.form.replay" type="textarea" :rows="4" placeholder="请输入回复内容"
show-word-limit maxlength="200" resize="none"/>
</el-form-item>
<div class="dialog-footer">
<el-button @click="state.replayDialog.show = false">取消</el-button>
<el-button type="primary" @click="commitReplay">确定</el-button>
</div>
</el-form>
</div>
</el-dialog>
</div>
</template>
@ -70,6 +89,19 @@ const state = reactive({
size: 10,
},
},
replayDialog: {
loading: false,
form: {
id: '',
replay: '',
},
show: false,
title: '回复',
message: '',
rules: {
replay: { required: true, message: '回复内容不能为空', trigger: 'blur' }
}
}
});
//
@ -110,6 +142,37 @@ const toShowDetail = (id: any) => {
messageDialogRef.value.openDialog(id);
}
const replayFormRef = ref();
//
const replay = (row: any) => {
state.replayDialog.message = row.message;
state.replayDialog.form.id = row.id;
state.replayDialog.show = true;
}
//
const commitReplay = () => {
try {
state.replayDialog.loading = true;
replayFormRef.value.validate(async(valid: boolean) => {
if (valid){
let res = await mesApi.updateMessage(state.replayDialog.form);
if(res?.success){
ElMessage.success('回复成功!');
state.replayDialog.show = false;
}else{ElMessage.error('回复失败!');}
}else{
ElMessage.error('请完善信息!');
}
});
} catch (error) {
ElMessage.error('回复失败!');
}finally{
state.replayDialog.loading = false;
}
}
//
const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
if(row.createtime === null) return '暂无';
@ -147,4 +210,14 @@ onMounted(() => {
}
}
}
.dialog-content{
padding: 0;
display: flex;
flex-direction: column;
}
.dialog-footer{
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -7,7 +7,7 @@
<div class="personal-user">
<div class="personal-user-left">
<el-upload class="h100 personal-user-left-upload" action="https://jsonplaceholder.typicode.com/posts/" multiple :limit="1">
<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
<img src="../../assets/图片2.png" />
</el-upload>
</div>
<div class="personal-user-right">
@ -206,6 +206,8 @@ const state = reactive<PersonalState>({
const currentTime = computed(() => {
return formatAxis(new Date());
});
//
const viteUrl = import.meta.env.VITE_API_URL;
</script>
<style scoped lang="scss">

View File

@ -43,16 +43,38 @@
</div>
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" label="图片名称" show-overflow-tooltip width="250px"></el-table-column>
<el-table-column prop="moduleName" label="模块" show-overflow-tooltip width="250px" align="center"></el-table-column>
<el-table-column prop="labelName" label="标签" show-overflow-tooltip width="250px" align="center"></el-table-column>
<el-table-column prop="uploadName" label="上传者" show-overflow-tooltip width="250px" align="center"></el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip width="350px" align="center"></el-table-column>
<el-table-column label="操作" width="300" align="center">
<el-table-column prop="name" label="图片名称" show-overflow-tooltip width="180px"></el-table-column>
<el-table-column label="素材" width="300px" align="center">
<template #default="scope">
<el-button size="small" text type="primary" @click="switchDrawer(2,scope.row.id)">查看</el-button>
<el-button size="small" text type="primary" @click="switchDrawer(1,scope.row.id)">编辑</el-button>
<el-button size="small" text type="primary" @click="deleteBanner(scope.row.id)">删除</el-button>
<el-image lazy :src="jpgFormatter(scope.row)" preview-teleported="true" :preview-src-list="[jpgFormatter(scope.row)]"/>
</template>
</el-table-column>
<el-table-column prop="moduleName" label="模块" show-overflow-tooltip width="200px" align="center"></el-table-column>
<el-table-column prop="labelName" label="标签" show-overflow-tooltip width="200px" align="center"></el-table-column>
<el-table-column prop="uploadName" label="上传者" show-overflow-tooltip width="200px" align="center"></el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip width="200px" align="center"></el-table-column>
<el-table-column label="操作" width="250" align="center">
<template #default="scope">
<el-button text type="primary" @click="switchDrawer(2,scope.row.id)">
<el-icon>
<ele-View />
</el-icon>
</el-button>
<el-button text type="primary" @click="switchDrawer(1,scope.row.id)">
<el-icon>
<ele-Edit />
</el-icon>
</el-button>
<el-button text type="primary" @click="download(scope.row)">
<el-icon>
<ele-Download />
</el-icon>
</el-button>
<el-button text type="primary" @click="deleteBanner(scope.row.id)">
<el-icon>
<ele-Delete />
</el-icon>
</el-button>
</template>
</el-table-column>
</el-table>
@ -70,7 +92,7 @@
style="max-width: 85%"
:model="state.formData"
:rules="state.rules"
label-width="100px"
label-width="110px"
class="demo-ruleForm"
:size="formSize"
status-icon
@ -164,6 +186,7 @@ import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
import type { ComponentSize, FormInstance, FormRules, UploadFile, UploadProps, UploadUserFile } from 'element-plus'
import { baseUrlHost } from '../../api/baseUrlHost';
import { photosApi } from '/@/api/photos';
import { downloadPhoto } from '../../api/download';
const router = useRouter();
@ -280,19 +303,14 @@ const viteUrl = import.meta.env.VITE_API_URL;
//
const jpgFormatter = (row: any) => {
console.log("44444444444",row);
let newPath;
if (row?.data) {
newPath = row.data.path.replaceAll('\\', '/')
console.log("11111111111",newPath);
} else {
newPath = row.path.replaceAll('\\\\', '/')
console.log("333333333333",newPath);
}
newPath = newPath.includes('http://8.138.171.103/') ? newPath : 'http://8.138.171.103/' + newPath
console.log("22222222222222222",newPath);
newPath = newPath.includes('http://8.138.171.103/') ? newPath : encodeURI(viteUrl + newPath)
return `${newPath}`;
}
@ -431,7 +449,28 @@ const handleRemove = (file: UploadFile, uploadFiles) => {
state.formData.path = "";
uploadDisabled.value = false;
}
};
//
const download = async(row:any) =>{
state.formData.loading = true;
let newPath;
if (row?.data) {
newPath = row.data.path.replaceAll('\\', '/')
} else {
newPath = row.path.replaceAll('\\\\', '/')
}
newPath = newPath.includes('http://localhost:8888/') ? newPath : viteUrl + newPath
try{
await downloadPhoto(newPath);
}catch(error){
console.error(error);
ElMessage.error('图片下载失败!')
}finally{
state.formData.loading = false;
}
};
//
const deleteBanner = (id: number) => {
ElMessageBox.confirm('确定要删除该图片吗?', '提示', {
@ -537,7 +576,15 @@ onMounted(() => {
flex: 1;
}
}
};
::v-deep .el-image {
width: 250px; //
height: 150px; //
object-fit: cover; //
display: block;
}
::v-deep .el-drawer .el-drawer__header {
margin: 15px 0 20px 0!important;
border-bottom: 0!important;

View File

@ -0,0 +1,96 @@
<template>
<div class="layout-pd system-dept-container">
<el-card class="apply-detail" v-loading="state.loading" shadow="hover">
<div class="top-box">
<div class="box-left">
<div class="tbl-left">
<img :src="viteUrl+(state.data.icon||defaultIcon)" :alt="state.data.name" />
</div>
<div class="tbl-right">
<div class="title">{{state.data.name}}</div>
<div class="tips">{{state.data.tips}}</div>
</div>
</div>
<div class="box-right">
</div>
</div>
<div class="bottom-box"></div>
</el-card>
</div>
</template>
<script setup lang="ts" name="applyDetail">
import { onMounted, reactive } from 'vue';
import { applyApi } from '/@/api/service/apply';
import { useRoute } from 'vue-router';
import { ElMessage } from 'element-plus';
//
const viteUrl = import.meta.env.VITE_API_URL;
const defaultIcon:string = 'static/pixel/home/default-work.png';
// api
const aplApi = applyApi();
//
const route = useRoute();
const id:any = route.query.id;
const state = reactive<any>({
data: {},
loading: false,
})
// -id
const getApplyDetailById = async(id:number) => {
try {
state.loading = true;
let res = await aplApi.getApplyDetail(id);
if(res?.success) {
state.data = res.data;
}
} catch (error) {
ElMessage.error('获取应用服务详情失败');
} finally {
state.loading = false;
}
}
onMounted(() => {
getApplyDetailById(id);
})
</script>
<style scoped lang="scss">
.system-dept-container {
height: 100%;
display: flex;
flex-direction: column;
.apply-detail{
flex: 1;
display: flex;
flex-direction: column;
.top-box{
display: flex;
margin: 0 20px;
background-color: aqua;
flex-wrap: wrap;
.box-left{
display: flex;
min-width: 60%;
background-color: aquamarine;
img{
width: 120px;
height: 120px;
}
}
.box-right{
min-width: 40%;
background-color: bisque;
}
}
}
}
</style>

View File

@ -0,0 +1,319 @@
<template>
<div class="system-menu-dialog-container">
<el-dialog @close="closeDialog" :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-form v-loading="state.loading" ref="applyDialogFormRef" :model="state.data" :rules="state.rules" size="default" label-width="100px">
<el-row :gutter="35">
<el-col :xs="24" :sm="10" :md="10" :lg="10" :xl="10" class="mb20">
<el-form-item label="图标:" class="iconImg">
<!-- 上传文件 -->
<el-upload ref="uploadRef" class="h100 personal-user-left-upload" action="#" list-type="picture-card" :file-list="state.fileArray"
:auto-upload="false" :limit="1" :class="{ hide: state.coverHide }" @change="handleUploadChange" accept='image/*'>
<template #default>
<el-icon><Plus /></el-icon>
</template>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
<el-icon><zoom-in /></el-icon>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
</el-upload>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="应用名称:" prop="name">
<el-input v-model="state.data.name" placeholder="请输入应用名称" clearable></el-input>
</el-form-item>
<el-form-item label="接口名:" prop="api">
<el-input v-model="state.data.api" placeholder="请输入接口名" clearable></el-input>
</el-form-item>
<el-form-item label="提示词:" prop="tips">
<el-input v-model="state.data.tips" placeholder="请输入提示词" clearable></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="35">
<el-col :xs="24" :sm="10" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="价格:" prop="price">
<el-input-number v-model="state.data.price" :min="0" controls-position="right">
<template #prefix><span></span></template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="排序号:" prop="position">
<el-input-number v-model="state.data.position" :min="1" controls-position="right" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="10" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="类型:" prop="type">
<el-select size="default" v-model="state.data.type" placeholder="请选择应用类型" clearable>
<el-option v-for="(item,index) in typeList" :key="index" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="是否上架:" prop="publish">
<el-switch v-model="state.data.publish" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16" class="mb20" v-if="state.data.options&&state.data.options.length>0">
<el-form-item label="选项:">
<div class="tags-option">
<el-tag v-for="option in state.data.options" :key="option.id" :disable-transitions="false">{{ option.optionName }}</el-tag>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button size="danger" plain v-if="state.dialog.type === 'edit'" @click="deleteApply"> </el-button>
<el-button type="primary" plain @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
<!-- 封面预览弹窗 -->
<el-dialog v-model="dialogVisible">
<img width="100%" w-full :src="dialogImageUrl" alt="预览" />
</el-dialog>
</div>
</template>
<script setup lang="ts" name="applyDialog">
import { ElMessage, ElMessageBox, UploadFile, UploadFiles } from 'element-plus';
import { applyApi } from '/@/api/service/apply';
import { articleApi } from '/@/api/article';
import { Delete, Plus, ZoomIn } from '@element-plus/icons-vue';
import { reactive, ref } from 'vue';
// /
const emit = defineEmits(['refresh']);
//
const applyDialogFormRef = ref();
const state:any = reactive({
data: {
},
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
loading: false,
rules: {
name: { required: true, message: '请输入应用名称', trigger: 'blur' },
api: { required: true, message: '请输入接口名', trigger: 'blur' },
tips: { required: true, message: '请输入提示词', trigger: 'blur' },
price: { required: true, message: '请输入价格', trigger: 'blur' },
position: { required: true, message: '请输入排序号', trigger: 'blur' },
type: { required: true, message: '请选择应用类型', trigger: 'blur' },
},
fileArray:[],//
coverFile: {},
coverHide: false
});
//
const typeList = ref([
{id: '0',name: '图生图'},
{id: '1',name: '文生图'},
{id: '2',name: '选项+图片'},
{id: '3',name: '选项+文字'},
{id: '4',name: '换装'},
]);
// api
const aplApi = applyApi();
const artApi = articleApi();
//
const disabled = ref(false);
//
const dialogImageUrl = ref('');
const dialogVisible = ref(false);
//
const viteUrl = import.meta.env.VITE_API_URL;
// upload/Attachment/20241207/0fb6372ed5cd4fb9be68315301093f57.png
const defaultIcon:string = 'static/pixel/home/default-work.png';
const uploadRef = ref();
//
const handleUploadChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
if(uploadFile.raw.type.includes('image')){
const file:any = uploadFile.raw;
state.coverFile = file;
state.coverHide = true;
}else{
ElMessage.error('请上传图片文件!');
uploadRef.value.clearFiles();
}
}
//
const handleRemove = (file?: UploadFile) => {
state.coverFile = {};
uploadRef.value.clearFiles();
state.coverHide = false;
}
//
const handlePictureCardPreview = (file: UploadFile) => {
dialogImageUrl.value = file.url!
dialogVisible.value = true
}
//
const getData = (id: number) => {
try {
state.loading = true;
aplApi.getApplyDetail(id).then((res: any) => {
if(!res?.success){
ElMessage.error('服务详情获取失败!');
return;
}
state.data = res.data;
state.data.publish = res.data.publish === 1;
const never:any = [{name:res.data.name,url:encodeURI(viteUrl+(res.data.icon||defaultIcon))}];
state.fileArray = never;
})
} catch (error) {
ElMessage.error('服务详情获取失败!');
} finally {
state.loading = false;
}
}
//
const openDialog = (type: string, row?: any) => {
if (type === 'edit') {
getData(row.id);
state.dialog.title = '修改应用';
state.dialog.submitTxt = '修 改';
state.coverHide = true;
} else {
state.dialog.title = '新增应用';
state.dialog.submitTxt = '新 增';
}
state.dialog.type = type;
state.dialog.isShowDialog = true;
};
//
const closeDialog = () => {
setTimeout(() => {
state.dialog.isShowDialog = false;
state.data = {};
state.fileArray = [];
handleRemove();
}, 300)
};
//
const onCancel = () => {
closeDialog();
};
//
const deleteApply = () => {
ElMessageBox.confirm(`确定要删除${state.data.name}吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async() => {
try {
state.loading = true;
let res = await aplApi.deleteApply(state.data.id);
if(res?.success){
ElMessage.success(`${state.data.name}删除成功!`);
closeDialog(); //
emit('refresh');
} else ElMessage.error(`${state.data.name}删除失败!`);
} catch(e) {
ElMessage.error('处理失败!');
} finally {
state.loading = false;
}
})
}
//
const uploadFile = async(file: any,form: any) => {
try {
state.loading = true;
const formdata = new FormData();
formdata.append('file', file);
let res = await artApi.uploadFile(formdata);
if(res?.success){
form.icon = res.data.path;
realSubmit(form);
}else{
ElMessage.error('封面上传失败!');
}
} catch (error) {
state.loading = false;
ElMessage.error('封面上传失败!');
}
}
//
const onSubmit = () => {
applyDialogFormRef.value.validate((valid: boolean) => {
if (valid && state.coverHide){
const form = {...state.data,publish:state.data.publish ? 1 : 0};
if(state.coverFile.type === undefined){
realSubmit(form);
}else{
uploadFile(state.coverFile,form);
}
}else{
ElMessage.error('请完善信息!');
}
});
};
// -
const realSubmit = async(form: any) => {
try {
state.loading = true;
let res = state.dialog.type === 'add' ? await aplApi.saveApply(form) : await aplApi.updateApply(form);
if(res?.success){
ElMessage.success('应用服务保存成功!');
closeDialog(); //
emit('refresh');
}else{
ElMessage.error('应用服务保存失败!');
}
} catch (error) {
ElMessage.error('处理失败!');
} finally {
state.loading = false;
}
}
//
defineExpose({
openDialog,
});
</script>
<style scoped lang="scss">
.iconImg :deep(.hide .el-upload--picture-card) {
display: none;
}
.tags-option{
width: 100%;
height: 90px;
overflow-y: scroll;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(50px,100px) );
grid-gap: 0.4em 0.8em;
// grid-auto-flow: column dense;
}
</style>

View File

@ -0,0 +1,399 @@
<template>
<div class="system-dept-container layout-pd">
<el-card
shadow="hover"
class="filtering-list br-top-no"
v-loading="state.tableData.loading"
element-loading-text="加载中..."
:class="{ 'min-h-360': state.tableData.data.length <= 0 }"
>
<div class="system-dept-search mb15">
应用名称<el-input size="default" placeholder="请输入应用名称" v-model="state.tableData.param.name"
class="ml10" style="max-width: 180px" clearable></el-input>
<el-button @click="getTableData" size="default" type="primary" class="ml10">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button @click="reset" size="default" type="primary" class="ml10">
<el-icon>
<ele-RefreshRight />
</el-icon>
重置
</el-button>
<el-button size="default" type="success" class="ml10" @click="addApply">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增应用
</el-button>
</div>
<div v-for="(val, key) in state.filtering" :key="key" ref="dlRefs" class="filtering-list-flex">
<div class="filtering-list-title">{{ val.title }}</div>
<div class="filtering-list-item" :style="{ height: val.isMore ? 'auto' : '50px' }">
<span class="span" :class="v.active ? 'dd-active' : ''" v-for="(v, k) in val.children" :key="k" @click="onSelItem(val, v)">
{{ v.label }}
</span>
<div class="dd-more" v-if="val.isShowMore" @click="val.isMore = !val.isMore">
<span>{{ val.isMore ? '收起' : '展开' }}</span>
<i :class="val.isMore ? 'el-icon-arrow-down' : 'el-icon-arrow-right'"></i>
</div>
</div>
</div>
<div class="flex-warp mt15 mb15" v-if="state.tableData.data.length > 0">
<el-row :gutter="15">
<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="4" class="mb15" v-for="(v, k) in state.tableData.data" :key="k" @click="onTableItemClick(v)">
<el-card class="flex-warp-item" shadow="hover" @click="toShowDetail(v)" :title="`¥ ${v.price}钻石`">
<div class="card-item-left">
<img :src="encodeURI(viteUrl + (v.icon||defaultIcon))" :alt="v.name">
</div>
<div class="card-item-right">
<div class="cir-one">{{ v.name }}</div>
<div class="cir-two">{{ v.tips || 'pixel.ai制图功能开始魔法~~' }}</div>
<div class="cir-three">
<el-tag type="info" round>{{ state.filtering[0].children[Number(v.type)+1].label }}</el-tag>
<div>{{ getDate(v.createtime) }}</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<div v-else class="filtering-no-data">
<div class="no-data-box">
<i class="el-icon-search"></i>
<div class="no-txt">暂无数据</div>
</div>
</div>
<template v-if="state.tableData.data.length > 0">
<div class="bottom-pagination">
<el-pagination
style="text-align: right"
background
@size-change="onHandleSizeChange"
@current-change="onHandleCurrentChange"
:page-sizes="[16, 32, 64]"
:current-page="state.tableData.param.current"
:page-size="state.tableData.param.size"
layout="total, sizes, prev, pager, next, jumper"
:total="state.tableData.total"
>
</el-pagination>
<!-- <div class="bottom-tips">tips右键应用 修改/删除</div> -->
</div>
</template>
</el-card>
<ApplyDialog ref="applyDialogRef" @refresh="getTableData()" />
</div>
</template>
<script setup lang="ts" name="apply">
import { ElMessage } from 'element-plus';
import { applyApi } from '/@/api/service/apply';
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
//
const viteUrl = import.meta.env.VITE_API_URL;
console.log('viteUrl:',viteUrl);
// upload/Attachment/20241207/0fb6372ed5cd4fb9be68315301093f57.png
const defaultIcon:string = 'static/pixel/home/default-work.png';
//
const ApplyDialog = defineAsyncComponent(() => import('/@/views/service/apply/dialog.vue'));
const applyDialogRef = ref();
//
const state: any = reactive({
//
filtering : [
{
title: '类型',
isMore: false,
isShowMore: false,
id: 0,
children: [
{id: '-1',label: '全部',active: true,},
{id: '0',label: '图生图',active: false,},
{id: '1',label: '文生图',active: false,},
{id: '2',label: '选项+图片',active: false,},
{id: '3',label: '选项+文字',active: false,},
{id: '4',label: '换装',active: false,},
],
},
{
title: '状态',
isMore: false,
isShowMore: false,
id: 1,
children: [
{id: '-1',label: '全部',active: true,},
{id: '0',label: '上架',active: false,},
{id: '1',label: '下架',active: false,}
],
}
],
tableData: {
data: [],
total: 0,
loading: false,
param: {
name: '',
current: 1,
size: 16,
},
},
});
// api
const aplApi = applyApi();
//
const onSelItem = (val: FilteringRowType, v: FilteringChilType) => {
val.children.map((v: FilteringChilType) => (v.active = false));
v.active = true;
state.tableData.loading = true;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onTableItemClick = (v: FilterListType) => {
};
//
const getDate = (val: string) => {
const date = new Date(val);
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
}
//
const reset = () =>{
state.tableData.param = {
name: '',
current: 1,
size: 16,
}
getTableData();
}
//
const getTableData = async() => {
try {
state.tableData.loading = true;
let res = await aplApi.getServicesList(state.tableData.param);
if(res?.success){
state.tableData.data = res.data.records;
state.tableData.total = res.data.total;
}else{
ElMessage.error('应用服务获取失败!');
}
} catch (error) {
} finally {
state.tableData.loading = false;
}
};
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.size = val;
// getTableData();
};
//
const onHandleCurrentChange = (val: number) => {
state.tableData.param.current = val;
// getTableData();
};
//
const addApply = () => {
applyDialogRef.value.openDialog('add');
}
// -
const toShowDetail = (row: any) => {
applyDialogRef.value.openDialog('edit',row);
// router.push({ path: '/apply/detail', query: { id:id } });
}
onMounted(() => {
getTableData();
})
</script>
<style scoped lang="scss">
.system-dept-container {
display: flex;
min-height: 92vh;
.filtering-list {
flex: 1;
overflow: hidden;
border-bottom: none !important;
.filtering-list-flex {
&:last-of-type {
.filtering-list-item {
border-bottom: none !important;
}
}
.filtering-list-title {
float: left;
width: 64px;
font-weight: 700;
position: relative;
color: #909399;
margin: 15px 0;
&:after {
content: '';
position: absolute;
border: 1px solid #909399;
border-width: 0 1px 1px 0;
width: 4px;
height: 4px;
transform: rotate(-45deg) translateY(-50%);
right: 10px;
top: 50%;
}
}
.filtering-list-item {
border-bottom: 1px dotted var(--next-border-color-light);
margin-left: 64px;
overflow: hidden;
position: relative;
.span {
color: #8d8d91;
font-size: 14px;
float: left;
padding: 0 15px;
margin: 15px 0;
&:hover {
color: var(--el-color-primary);
cursor: pointer;
}
}
.dd-active {
color: var(--el-color-primary);
}
.dd-more {
font-size: 12px;
position: absolute;
right: 0;
top: 16px;
color: #a5a5a5;
&:hover {
cursor: pointer;
color: #8d8d91;
}
}
}
}
}
.br-top-no {
border-top: none;
.flex-warp {
flex: 1;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
margin: 0 -5px;
.el-row {
width: 101%;
}
.flex-warp-item {
padding: 5px;
height: 120px;
border-radius: 12px;
cursor: pointer;
&:hover{
margin-top: -10px;
}
.card-item-left{
width: 80px;
height: 80px;
img{
width: 100%;
height: 100%;
}
margin-right: 14px;
}
.card-item-right{
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
white-space: nowrap;
.cir-one{
font-size: 1.3em;
font-weight: bold;
color: rgba($color: #000000, $alpha: 0.8);
margin-bottom: 0.6em;
text-overflow: ellipsis;
overflow: hidden;
}
.cir-two{
font-size: 1.15em;
color: #8f969c;
margin-bottom: 0.6em;
text-overflow: ellipsis;
overflow: hidden;
}
.cir-three{
display: flex;
align-items: center;
justify-content: space-between;
color: #8f969c;
div{
text-overflow: ellipsis;
overflow: hidden;
}
}
}
}
:deep(.el-card__body) {
flex-direction: row;
align-items: center;
padding-top: 8px;
padding-bottom: 8px;
}
}
:deep(.el-card__body) {
height: 100%;
// width: 100%;
display: flex;
flex-direction: column;
.filtering-no-data {
display: flex;
height: 100%;
.no-data-box {
color: #cccccc;
margin: auto;
i {
font-size: 70px;
}
.no-txt {
font-size: 14px;
text-align: center;
margin-top: 15px;
}
}
}
}
}
.min-h-360 {
// height: 360px;
}
}
.bottom-pagination{
display: flex;
align-items: center;
justify-content: space-between;
.bottom-tips{
margin-right: 10px;
color: #8d8d91;
}
}
</style>

View File

@ -5,7 +5,7 @@
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色名称">
<el-input v-model="state.ruleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
<el-input v-model="state.ruleForm.name" placeholder="请输入角色名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
@ -15,22 +15,22 @@
<span>角色标识</span>
</el-tooltip>
</template>
<el-input v-model="state.ruleForm.roleSign" placeholder="请输入角色标识" clearable></el-input>
<el-input v-model="state.ruleForm.acgroup" placeholder="请输入角色标识" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="state.ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
<el-input-number v-model="state.ruleForm.id" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色状态">
<el-switch v-model="state.ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
<el-switch v-model="state.ruleForm.state" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="角色描述">
<el-input v-model="state.ruleForm.describe" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
<el-input v-model="state.ruleForm.description" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
@ -54,17 +54,17 @@
import { reactive, ref } from 'vue';
// /
const emit = defineEmits(['refresh']);
const emit = defineEmits(['reset']);
//
const roleDialogFormRef = ref();
const state = reactive({
ruleForm: {
roleName: '', //
roleSign: '', //
sort: 0, //
status: true, //
describe: '', //
name: '', //
acgroup: '', //
id: 0, //
state: true, //
description: '', //
},
menuData: [] as TreeType[],
menuProps: {
@ -80,7 +80,7 @@ const state = reactive({
});
//
const openDialog = (type: string, row: RowRoleType) => {
const openDialog = (type: string, row: any) => {
if (type === 'edit') {
state.ruleForm = row;
state.dialog.title = '修改角色';
@ -107,7 +107,7 @@ const onCancel = () => {
//
const onSubmit = () => {
closeDialog();
emit('refresh');
emit('reset');
// if (state.dialog.type === 'add') { }
};
//

View File

@ -18,23 +18,23 @@
</div>
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%">
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="roleName" label="角色名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="roleSign" label="角色标识" show-overflow-tooltip></el-table-column>
<el-table-column prop="sort" label="排序" show-overflow-tooltip></el-table-column>
<el-table-column prop="status" label="角色状态" show-overflow-tooltip>
<el-table-column prop="name" label="角色名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="acgroup" label="角色标识" show-overflow-tooltip></el-table-column>
<el-table-column prop="id" label="排序" show-overflow-tooltip></el-table-column>
<el-table-column prop="state" label="角色状态" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.status">启用</el-tag>
<el-tag type="success" v-if="scope.row.state">启用</el-tag>
<el-tag type="info" v-else>禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="describe" label="角色描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column prop="description" label="角色描述" show-overflow-tooltip></el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onOpenEditRole('edit', scope.row)"
<el-button :disabled="scope.row.name === '管理员'" size="small" text type="primary" @click="onOpenEditRole('edit', scope.row)"
>修改</el-button
>
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
<el-button :disabled="scope.row.name === '管理员'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -52,17 +52,20 @@
>
</el-pagination>
</div>
<RoleDialog ref="roleDialogRef" @refresh="getTableData()" />
<RoleDialog ref="roleDialogRef" @reset="reset" />
</div>
</template>
<script setup lang="ts" name="systemRole">
import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { ElMessageBox, ElMessage,TableColumnCtx } from 'element-plus';
import { RoleApi } from '/@/api/role';
//
const RoleDialog = defineAsyncComponent(() => import('/@/views/usermanage/role/dialog.vue'));
const roleapi = RoleApi();
//
const roleDialogRef = ref();
const state = reactive<SysRoleState>({
@ -78,24 +81,39 @@ const state = reactive<SysRoleState>({
},
});
//
const getTableData = () => {
const getTableData = async() => {
state.tableData.loading = true;
const data = [];
for (let i = 0; i < 20; i++) {
data.push({
roleName: i === 0 ? '超级管理员' : '普通用户',
roleSign: i === 0 ? 'admin' : 'common',
describe: `测试角色${i + 1}`,
sort: i,
status: true,
createTime: new Date().toLocaleString(),
});
// const data = [];
// for (let i = 0; i < 20; i++) {
// data.push({
// roleName: i === 0 ? '' : '',
// roleSign: i === 0 ? 'admin' : 'common',
// describe: `${i + 1}`,
// sort: i,
// status: true,
// createTime: new Date().toLocaleString(),
// });
// }
// state.tableData.data = data;
// state.tableData.total = state.tableData.data.length;
try{
const res = await roleapi.getRoleList(state.tableData.param);
// console.log(res.data);
if(res?.success){
state.tableData.data = res.data.records;
state.tableData.total = res.data.total;
// console.log(state.tableData.data);
}else{
ElMessage.error('角色列表获取失败!');
}
}catch(error){
console.error(error);
}finally{
setTimeout(() => {
state.tableData.loading = false;
}, 500);
}
state.tableData.data = data;
state.tableData.total = state.tableData.data.length;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onOpenAddRole = (type: string) => {
@ -106,18 +124,49 @@ const onOpenEditRole = (type: string, row: Object) => {
roleDialogRef.value.openDialog(type, row);
};
//
const onRowDel = (row: RowRoleType) => {
ElMessageBox.confirm(`此操作将永久删除角色名称:“${row.roleName}”,是否继续?`, '提示', {
const onRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除角色名称:“${row.name}”,是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
getTableData();
ElMessage.success('删除成功');
.then(async() => {
// getTableData();
// ElMessage.success('');
try{
state.tableData.loading = true;
const res = await roleapi.deleteRole(row.id);
if(res?.success){
reset();
ElMessage.success('角色删除成功!');
} else ElMessage.error('角色删除失败!');
}catch(error){
ElMessage.error('处理失败!');
}finally{
state.tableData.loading = false;
}
})
.catch(() => {});
};
//
const reset = (index = 0) =>{
state.tableData.param = {
// moduleid:moduleList.value
pageNum: index ? state.tableData.param.pageNum : 1,
pageSize: 10,
},
console.log(index,state.tableData.param);
getTableData();
};
//
const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
let date = new Date(row.createtime);
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
};
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;

View File

@ -301,7 +301,8 @@
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.logoSetting') }}</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-upload ref="uploadRef" action="#" :limit="1" :auto-upload="false" @change="onWartermarkLogoUpload" :on-preview="handlePreview"
:file-list="getThemeConfig.logoMini?[{name:getThemeConfig.globalTitle,url:getThemeConfig.logoMini}]:[]" accept='image/*' :on-remove="handleRemove">
:file-list="getThemeConfig.logoMini?[{name:getThemeConfig.globalTitle,url:getThemeConfig.logoMini}]:
[]" accept='image/*' :on-remove="handleRemove">
<template #default>
<el-button v-if="!getThemeConfig.logoMini" class="logo-picture" type="text">上传文件</el-button>
</template>
@ -475,7 +476,7 @@ import commonFunction from '/@/utils/commonFunction';
import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
import { articleApi } from '/@/api/article';
import logoMini from '/@/assets/logo-mini.svg';
import logoMini from '/@/assets/logo.png';
const state = reactive({
isMobile: false,
@ -643,8 +644,8 @@ const handlePreview: UploadProps['onPreview'] = (uploadFile) =>{
};
const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
// onWartermarkLogoMiniInput('/src/assets/logo-mini.svg');
onWartermarkLogoMiniInput('');
uploadRef.value.clearFiles();
onWartermarkLogoMiniInput('');
}
//
const viteUrl = import.meta.env.VITE_API_URL;
@ -722,6 +723,8 @@ const onCopyConfigClick = () => {
//
const onResetConfigClick = () => {
Local.clear();
onWartermarkLogoMiniInput('');
uploadRef.value.clearFiles();
window.location.reload();
// @ts-ignore
Local.set('version', __NEXT_VERSION__);

View File

@ -15,6 +15,9 @@ const alias: Record<string, string> = {
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
};
// 请求映射后端路径
const url:string = 'http://8.138.171.103';
const viteConfig = defineConfig((mode: ConfigEnv) => {
const env = loadEnv(mode.mode, process.cwd());
@ -31,7 +34,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
hmr: true,
proxy: {
'/vueAdminApi': {
target: 'http://8.138.171.103:8081',
target: url+':8081',
// target: 'http://8.138.175.106:8090',
// target: 'https://www.ymsc.org.cn/vueAdminApi',
ws: true,
@ -40,7 +43,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
rewrite: (path) => path.replace(/^\/vueAdminApi/, ''),
},
'/pixelApi': {
target: 'http://8.138.171.103:8082',
target: url+'/pixelApi',
ws: true,
secure: false,
changeOrigin: true,
@ -56,13 +59,27 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
rewrite: (path) => path.replace(/^\/vueAdmin/, ''),
},
'/upload': {
target: 'http://8.138.171.103',
target: url,
// target: 'http://8.138.175.106',
// target: 'https://www.ymsc.org.cn/upload',
secure: false,
ws: true,
changeOrigin: true,
},
'/aiupload': {
target: url,
// target: 'http://8.138.175.106',
// target: 'https://www.ymsc.org.cn/upload',
secure: false,
ws: true,
changeOrigin: true,
},
'/static': {
target: url,
secure: false,
ws: true,
changeOrigin: true,
},
'/gitee': {
target: 'https://gitee.com',
ws: true,