Merge remote-tracking branch 'origin/lei_dev' into leng_dev

# Conflicts:
#	package.json
This commit is contained in:
lbw 2023-03-03 17:59:26 +08:00
commit b68eaadfb4
11 changed files with 720 additions and 666 deletions

48
doc/SvgIcon.md Normal file
View File

@ -0,0 +1,48 @@
## 全局组件
- 组件支持`四种`svg图标
- `四种`图标均支持 color 和 size 属性,使用示例见下文:
```mermaid
# font-awesome的图标使用 fa 作为前缀(带个空格)
<SvgIcon name="fa fa-pencil" />
# element-plus 的图标,使用 el-icon 作为前缀,图标名称请使用:首字母大写的驼峰语法
<SvgIcon name="ele-icon-Close" color="#8595F4" size="20" />
# 本地`/src/assets/icons`文件夹内的SVG图标使用 local 作为前缀,文件名作为后缀
# 文件自动加载,新增请重新编译
<SvgIcon name="local-logo" />
# 阿里iconfont的图标使用 iconfont 作为前缀(带个空格)
<SvgIcon name="iconfont icon-user" size="20" />
```
有时Icon组件会覆盖不了图标的原有颜色或大小请检查SVG文件的源代码。
## 图标添加与配置
### 本地图标
1. 将SVG图标文件放入本地`/src/assets/icons`文件夹,然后重新编译项目。
2. 系统会自动加载该目录下的所有图标文件备用,现在你可以使用`Icon`组件来显示图标了。
3. 示例:`<SvgIcon name="local-SVG的文件名" />`
4. 不建议在此文件夹放置`非常大、非常多`的文件,可能会影响系统加载速度,当确有此方面需求时,请将文件放置到其他位置,并单独导入和使用图标。
### font-awesome 图标
1. 此图标库目前 `pigx-ui` 已经默认加载。加载代码在 `/src/utils/setIconfont.ts`中的cssUrls中。
2. 你可以直接使用font-awesome 4.7.0的所有图标,这些图标可以在这里 https://fontawesome.com.cn/faicons/ 找到,当然你也可以直接使用图标选择器寻找图标。
3. 示例:<SvgIcon name="fa fa-pencil" />注意图标名称一定是以fa加一个空格开头
### element-plus 图标
1. 此图标库目前 `pigx-ui` 已经默认加载,加载代码在 `/src/main.ts` 执行的 elementIcons 方法中。
2. 你可以直接使用 `element-plus/icons-vue ^2.0.10` 的所有图标,在使用 `element-plus` 图标时,请使用`ele` 作为前缀,图标名称请使用:首字母大写的驼峰语法。
3. 示例:<SvgIcon name="ele-RefreshRight" />
### iconfont 图标
1. 目前系统未使用任何iconfont图标你可以获取iconfont图标库项目的Font class链接后设置到 `/src/utils/setIconfont.ts` 中的 `cssUrls` 中,系统会自动加载链接中的所有图标以供使用。
2. 示例:`<SvgIcon name="iconfont icon-user" />`注意图标名称一定是以iconfont加一个空格开头以图标名称结尾。

View File

@ -1,86 +1,86 @@
{ {
"name": "pigx-ui-pro", "name": "pigx-ui-pro",
"version": "1.0.0", "version": "1.0.0",
"description": "PIGCLOUD微服务开发平台", "description": "PIGCLOUD微服务开发平台",
"author": "pig4cloud", "author": "pig4cloud",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "vite --force", "dev": "vite --force",
"build": "vite build", "build": "vite build",
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
"prettier": "prettier --write ." "prettier": "prettier --write ."
}, },
"dependencies": { "dependencies": {
"@chenfengyuan/vue-qrcode": "^2.0.0", "@chenfengyuan/vue-qrcode": "^2.0.0",
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.0.10",
"@highlightjs/vue-plugin": "^2.1.0", "@highlightjs/vue-plugin": "^2.1.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.2.1", "axios": "^1.3.3",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"echarts": "^5.4.1", "echarts": "^5.4.1",
"element-plus": "^2.2.26", "element-plus": "^2.2.32",
"form-designer": "^0.0.2", "form-designer": "^0.0.2",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.28", "pinia": "^2.0.32",
"qrcode": "1.5.1", "qrcode": "1.5.1",
"qs": "^6.11.0", "qs": "^6.11.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"vue": "3.2.47", "vue": "^3.2.47",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vxe-table": "^4.3.5", "vxe-table": "^4.3.5",
"xe-utils": "^3.5.4" "xe-utils": "^3.5.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.11.13", "@types/node": "^18.14.0",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/sortablejs": "^1.15.0", "@types/sortablejs": "^1.15.0",
"@typescript-eslint/eslint-plugin": "^5.46.0", "@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.46.0", "@typescript-eslint/parser": "^5.53.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue/compiler-sfc": "^3.2.45", "@vue/compiler-sfc": "^3.2.47",
"consola": "^2.15.3", "consola": "^2.15.3",
"eslint": "8.22.0", "eslint": "^8.34.0",
"eslint-plugin-vue": "^9.8.0", "eslint-plugin-vue": "^9.9.0",
"pinia-plugin-persist": "^1.0.0", "pinia-plugin-persist": "^1.0.0",
"prettier": "^2.8.4", "prettier": "2.8.4",
"sass": "^1.56.2", "sass": "^1.58.3",
"typescript": "^4.9.4", "typescript": "^4.9.5",
"unplugin-auto-import": "^0.13.0", "unplugin-auto-import": "^0.13.0",
"vite": "^4.0.0", "vite": "^4.1.4",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-style-import": "^2.0.0", "vite-plugin-style-import": "^2.0.0",
"vite-plugin-top-level-await": "^1.2.4", "vite-plugin-top-level-await": "^1.2.4",
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.1.0" "vue-eslint-parser": "^9.1.0"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
"last 2 versions", "last 2 versions",
"not dead" "not dead"
], ],
"bugs": { "bugs": {
"url": "https://pig4cloud.com" "url": "https://pig4cloud.com"
}, },
"engines": { "engines": {
"node": ">=16.0.0", "node": ">=16.0.0",
"npm": ">= 7.0.0" "npm": ">= 7.0.0"
}, },
"keywords": [ "keywords": [
"vue", "vue",
"vue3", "vue3",
"vuejs/vue-next", "vuejs/vue-next",
"element-ui", "element-ui",
"element-plus" "element-plus"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://gitee.com/log4j/pig" "url": "https://gitee.com/log4j/pig"
} }
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n"> <el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
<router-view v-show="themeConfig.lockScreenTime > 1" /> <router-view v-show="setLockScreen" />
<LockScreen v-if="themeConfig.isLockScreen" /> <LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" /> <Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
<CloseFull v-if="!themeConfig.isLockScreen" /> <CloseFull v-if="!themeConfig.isLockScreen" />
@ -32,6 +32,15 @@ const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
//
const setLockScreen = computed(() => {
//
// https://gitee.com/lyt-top/vue-next-admin/issues/I6AF8P
return themeConfig.value.isLockScreen ? themeConfig.value.lockScreenTime > 1 : themeConfig.value.lockScreenTime >= 0;
});
// //
const getGlobalComponentSize = computed(() => { const getGlobalComponentSize = computed(() => {
return other.globalComponentSize(); return other.globalComponentSize();

View File

@ -10,7 +10,7 @@ const clearReturn = /(\r)|(\n)/g;
const clearFill = /(fill="[^>+].*?")/g; const clearFill = /(fill="[^>+].*?")/g;
function findSvgFile(dir: string): string[] { function findSvgFile(dir: string): string[] {
const svgRes = []; const svgRes = [] as any;
const dirents = readdirSync(dir, { const dirents = readdirSync(dir, {
withFileTypes: true, withFileTypes: true,
}); });

View File

@ -1,317 +1,308 @@
<template> <template>
<el-tabs v-model="objData.repType" type="border-card" @tab-click="handleClick" style="width: 100%;"> <el-tabs v-model="objData.repType" type="border-card" @tab-click="handleClick" style="width: 100%">
<el-tab-pane name="text" label="text"> <el-tab-pane name="text" label="text">
<template #label><i class="el-icon-document"></i> 文本</template> <template #label><i class="el-icon-document"></i> 文本</template>
<el-input <el-input v-model="objData.repContent" type="textarea" :rows="5" placeholder="请输入内容"> </el-input>
v-model="objData.repContent" </el-tab-pane>
type="textarea"
:rows="5"
placeholder="请输入内容">
</el-input>
</el-tab-pane>
<el-tab-pane name="image" label="image"> <el-tab-pane name="image" label="image">
<template #label><i class="el-icon-picture"></i> 图片</template> <template #label><i class="el-icon-picture"></i> 图片</template>
<el-row> <el-row>
<div v-if="objData.repUrl" class="select-item"> <div v-if="objData.repUrl" class="select-item">
<img class="material-img" :src="objData.repUrl"> <img class="material-img" :src="objData.repUrl" />
<p v-if="objData.repName" class="item-name">{{ objData.repName }}</p> <p v-if="objData.repName" class="item-name">{{ objData.repName }}</p>
<el-row class="ope-row"> <el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button> <el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
</el-row> </el-row>
</div> </div>
<div v-if="!objData.repUrl" style="width: 100%;"> <div v-if="!objData.repUrl" style="width: 100%">
<el-row style="text-align: center"> <el-row style="text-align: center">
<el-col :span="12" class="col-select"> <el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial({type: 'image',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i> <el-button type="success" @click="openMaterial({ type: 'image', accountId: props.objData.appId })"
</el-button> >素材库选择<i class="fansel-icon--right"></i>
</el-col> </el-button>
<el-col :span="12" class="col-add"> </el-col>
<wx-file-upload :data="uploadData" @success="handelUpload"></wx-file-upload> <el-col :span="12" class="col-add">
</el-col> <wx-file-upload :data="uploadData" @success="handelUpload"></wx-file-upload>
</el-row> </el-col>
</div> </el-row>
</el-row> </div>
</el-tab-pane> </el-row>
</el-tab-pane>
<el-tab-pane name="voice" label="voice"> <el-tab-pane name="voice" label="voice">
<template #label><i class="el-icon-phone"></i> 语音</template> <template #label><i class="el-icon-phone"></i> 语音</template>
<el-row> <el-row>
<div v-if="objData.repName" class="select-item"> <div v-if="objData.repName" class="select-item">
<p class="item-name">{{ objData.repName }}</p> <p class="item-name">{{ objData.repName }}</p>
<div class="item-infos"> <div class="item-infos">
<img :src="WxVoice" style="width: 100px" @click="loadVideo(item)"> <img :src="WxVoice" style="width: 100px" @click="loadVideo(item)" />
</div> </div>
<el-row class="ope-row"> <el-row class="ope-row">
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button> <el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
</el-row> </el-row>
</div> </div>
<div v-if="!objData.repName" style="width: 100%;"> <div v-if="!objData.repName" style="width: 100%">
<el-row style="text-align: center"> <el-row style="text-align: center">
<el-col :span="12" class="col-select"> <el-col :span="12" class="col-select">
<el-button type="success" @click="openMaterial({type: 'voice',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i> <el-button type="success" @click="openMaterial({ type: 'voice', accountId: props.objData.appId })"
</el-button> >素材库选择<i class="fansel-icon--right"></i>
</el-col> </el-button>
<el-col :span="12" class="col-add"> </el-col>
<wx-file-upload :data="uploadData" @success="handelUpload"></wx-file-upload> <el-col :span="12" class="col-add">
</el-col> <wx-file-upload :data="uploadData" @success="handelUpload"></wx-file-upload>
</el-row> </el-col>
</div> </el-row>
</el-row> </div>
</el-tab-pane> </el-row>
</el-tab-pane>
<el-tab-pane name="video" label="video"> <el-tab-pane name="video" label="video">
<template #label><i class="el-icon-share"></i> 视频</template> <template #label><i class="el-icon-share"></i> 视频</template>
<el-row style="text-align: center;flex: 1"> <el-row style="text-align: center; flex: 1">
<el-input v-model="objData.repName" placeholder="请输入标题"></el-input> <el-input v-model="objData.repName" placeholder="请输入标题"></el-input>
<el-input v-model="objData.repDesc" placeholder="请输入描述"></el-input> <el-input v-model="objData.repDesc" placeholder="请输入描述"></el-input>
<div style="text-align: center;"> <div style="text-align: center">
<a v-if="objData.repUrl" target="_blank" :href="objData.repUrl"><i <a v-if="objData.repUrl" target="_blank" :href="objData.repUrl"><i class="icon-bofang">&nbsp;播放视频</i></a>
class="icon-bofang">&nbsp;播放视频</i></a> </div>
</div> <div style="margin: 20px 0"></div>
<div style="margin: 20px 0;"></div> <div style="text-align: center">
<div style="text-align: center"> <el-button type="success" @click="openMaterial({ type: 'video', accountId: props.objData.appId })"
<el-button type="success" @click="openMaterial({type: 'video',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i> >素材库选择<i class="fansel-icon--right"></i>
</el-button> </el-button>
</div> </div>
</el-row> </el-row>
</el-tab-pane>
</el-tab-pane> <el-tab-pane name="news" label="news">
<template #label><i class="el-icon-news"></i> 图文</template>
<el-tab-pane name="news" label="news"> <el-row>
<template #label><i class="el-icon-news"></i> 图文</template> <div v-if="objData.content" class="select-item">
<el-row> <wx-news :obj-data="objData.content.newsItem"></wx-news>
<div v-if="objData.content" class="select-item"> <el-row class="ope-row">
<wx-news :obj-data="objData.content.newsItem"></wx-news> <el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button>
<el-row class="ope-row"> </el-row>
<el-button type="danger" icon="el-icon-delete" circle @click="deleteObj"></el-button> </div>
</el-row> <div v-if="!objData.content" style="width: 100%">
</div> <el-row style="text-align: center">
<div v-if="!objData.content" style="width: 100%;"> <el-col :span="24" class="col-select2">
<el-row style="text-align: center"> <el-button type="success" @click="openMaterial({ type: 'news', accountId: props.objData.appId })"
<el-col :span="24" class="col-select2"> >素材库选择<i class="fansel-icon--right"></i>
<el-button type="success" @click="openMaterial({type: 'news',accountId: props.objData.appId})">素材库选择<i class="fansel-icon--right"></i> </el-button>
</el-button> </el-col>
</el-col> </el-row>
</el-row> </div>
</div> </el-row>
</el-row> </el-tab-pane>
</el-tab-pane> </el-tabs>
</el-tabs>
<wx-material-select ref="dialogNewsRef" @selectMaterial="selectMaterial"></wx-material-select>
<wx-material-select ref="dialogNewsRef" @selectMaterial="selectMaterial"></wx-material-select>
</template> </template>
<script setup lang="ts" name="wx-reply"> <script setup lang="ts" name="wx-reply">
import {getMaterialVideo} from "/@/api/mp/wx-material"; import { getMaterialVideo } from '/@/api/mp/wx-material';
import {useMessage} from "/@/hooks/message"; import { useMessage } from '/@/hooks/message';
import WxVideo from '/@/assets/icon/wx-video.svg' import WxVideo from '/@/assets/icon/wx-video.svg';
import WxVoice from '/@/assets/icon/wx-voice.svg' import WxVoice from '/@/assets/icon/wx-voice.svg';
const WxMaterialSelect = defineAsyncComponent(() => import("/@/components/wechart/wx-material-select/main.vue")) const WxMaterialSelect = defineAsyncComponent(() => import('/@/components/wechart/wx-material-select/main.vue'));
const WxFileUpload = defineAsyncComponent(() => import("/@/components/wechart/fileUpload/index.vue")) const WxFileUpload = defineAsyncComponent(() => import('/@/components/wechart/fileUpload/index.vue'));
const WxNews = defineAsyncComponent(() => import("/@/components/wechart/wx-news/index.vue"))
const WxNews = defineAsyncComponent(() => import('/@/components/wechart/wx-news/index.vue'));
const props = defineProps({ const props = defineProps({
objData: { objData: {
type: Object, type: Object,
default: () => { default: () => {
return { return {
repType: '', repType: '',
repContent: '', repContent: '',
repName: '', repName: '',
repDesc: '', repDesc: '',
repUrl: '' repUrl: '',
} };
} },
} },
}) });
const uploadData = reactive({ const uploadData = reactive({
mediaType: props.objData.repType, mediaType: props.objData.repType,
title: '', title: '',
introduction: '', introduction: '',
appId: props.objData.appId appId: props.objData.appId,
}) });
const tempObj = ref() as any const tempObj = ref() as any;
const handleClick = (tab) => { const handleClick = (tab) => {
uploadData.mediaType = tab.paneName uploadData.mediaType = tab.paneName;
uploadData.appId = props.objData.appId uploadData.appId = props.objData.appId;
const tempObjItem = tempObj.value[props.objData.repType] const tempObjItem = tempObj.value[props.objData.repType];
if (tempObjItem) { if (tempObjItem) {
props.objData.repName = tempObjItem.repName ? tempObjItem.repName : null props.objData.repName = tempObjItem.repName ? tempObjItem.repName : null;
props.objData.repMediaId = tempObjItem.repMediaId ? tempObjItem.repMediaId : null props.objData.repMediaId = tempObjItem.repMediaId ? tempObjItem.repMediaId : null;
props.objData.media_id = tempObjItem.media_id ? tempObjItem.media_id : null props.objData.media_id = tempObjItem.media_id ? tempObjItem.media_id : null;
props.objData.repUrl = tempObjItem.repUrl ? tempObjItem.repUrl : null props.objData.repUrl = tempObjItem.repUrl ? tempObjItem.repUrl : null;
props.objData.content = tempObjItem.content ? tempObjItem.content : null props.objData.content = tempObjItem.content ? tempObjItem.content : null;
props.objData.repDesc = tempObjItem.repDesc ? tempObjItem.repDesc : null props.objData.repDesc = tempObjItem.repDesc ? tempObjItem.repDesc : null;
} else { } else {
props.objData.repName = '' props.objData.repName = '';
props.objData.repMediaId = '' props.objData.repMediaId = '';
props.objData.media_id = '' props.objData.media_id = '';
props.objData.repUrl = '' props.objData.repUrl = '';
props.objData.content = '' props.objData.content = '';
props.objData.repDesc = '' props.objData.repDesc = '';
} }
};
}
const deleteObj = () => { const deleteObj = () => {
props.objData.repName = '' props.objData.repName = '';
props.objData.repUrl = '' props.objData.repUrl = '';
props.objData.content = '' props.objData.content = '';
} };
const openMaterial = (data: any) => { const openMaterial = (data: any) => {
dialogNewsRef.value.openDialog(data) dialogNewsRef.value.openDialog(data);
} };
const dialogNewsRef = ref() const dialogNewsRef = ref();
const selectMaterial = (item, appId) => { const selectMaterial = (item, appId) => {
let tempObjItem = { let tempObjItem = {
repType: '', repType: '',
repMediaId: '', repMediaId: '',
media_id: '', media_id: '',
content: '' content: '',
} as any } as any;
tempObjItem.repType = props.objData.repType tempObjItem.repType = props.objData.repType;
tempObjItem.repMediaId = item.mediaId tempObjItem.repMediaId = item.mediaId;
tempObjItem.media_id = item.mediaId tempObjItem.media_id = item.mediaId;
tempObjItem.content = item.content tempObjItem.content = item.content;
props.objData.repMediaId = item.mediaId props.objData.repMediaId = item.mediaId;
props.objData.media_id = item.mediaId props.objData.media_id = item.mediaId;
props.objData.content = item.content props.objData.content = item.content;
if (props.objData.repType === 'music') { if (props.objData.repType === 'music') {
tempObjItem.repThumbMediaId = item.mediaId tempObjItem.repThumbMediaId = item.mediaId;
tempObjItem.repThumbUrl = item.url tempObjItem.repThumbUrl = item.url;
props.objData.repThumbMediaId = item.mediaId props.objData.repThumbMediaId = item.mediaId;
props.objData.repThumbUrl = item.url props.objData.repThumbUrl = item.url;
} else { } else {
tempObjItem.repName = item.name tempObjItem.repName = item.name;
tempObjItem.repUrl = item.url tempObjItem.repUrl = item.url;
props.objData.repName = item.name props.objData.repName = item.name;
props.objData.repUrl = item.url props.objData.repUrl = item.url;
} }
if (props.objData.repType === 'video') { if (props.objData.repType === 'video') {
getMaterialVideo({ getMaterialVideo({
mediaId: item.mediaId, mediaId: item.mediaId,
appId: appId appId: appId,
}).then(response => { }).then((response) => {
const data = response.data const data = response.data;
tempObjItem.repDesc = data.description || '' tempObjItem.repDesc = data.description || '';
tempObjItem.repUrl = data.downUrl tempObjItem.repUrl = data.downUrl;
props.objData.repName = data.title props.objData.repName = data.title;
props.objData.repDesc = data.description || '' props.objData.repDesc = data.description || '';
props.objData.repUrl = data.downUrl props.objData.repUrl = data.downUrl;
}) });
} }
tempObj.value[props.objData.repType] = tempObjItem tempObj.value[props.objData.repType] = tempObjItem;
} };
const handelUpload = (response) => { const handelUpload = (response) => {
if (response.code === 0) { if (response.code === 0) {
const item = response.data const item = response.data;
selectMaterial(item,props.objData.appId) selectMaterial(item, props.objData.appId);
} else { } else {
useMessage().error("上传错误" + response.msg) useMessage().error('上传错误' + response.msg);
} }
} };
const loadVideo = (item) => { const loadVideo = (item) => {
getMaterialVideo({ getMaterialVideo({
mediaId: item.repMediaId, mediaId: item.repMediaId,
appId: item.appId appId: item.appId,
}).then(response => { }).then((response) => {
const data = response.data const data = response.data;
window.open(data.downUrl,'target',''); window.open(data.downUrl, 'target', '');
}) });
} };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.select-item { .select-item {
width: 280px; width: 280px;
padding: 10px; padding: 10px;
margin: 0 auto 10px auto; margin: 0 auto 10px auto;
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
} }
.select-item2 { .select-item2 {
padding: 10px; padding: 10px;
margin: 0 auto 10px auto; margin: 0 auto 10px auto;
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
} }
.ope-row { .ope-row {
padding-top: 10px; padding-top: 10px;
text-align: center; text-align: center;
} }
.item-name { .item-name {
font-size: 12px; font-size: 12px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
} }
.el-form-item__content { .el-form-item__content {
line-height: unset !important; line-height: unset !important;
} }
.col-select { .col-select {
border: 1px solid rgb(234, 234, 234); border: 1px solid rgb(234, 234, 234);
padding: 50px 0px; padding: 50px 0px;
height: 160px; height: 160px;
width: 49.5%; width: 49.5%;
} }
.col-select2 { .col-select2 {
border: 1px solid rgb(234, 234, 234); border: 1px solid rgb(234, 234, 234);
padding: 50px 0px; padding: 50px 0px;
height: 160px; height: 160px;
} }
.col-add { .col-add {
border: 1px solid rgb(234, 234, 234); border: 1px solid rgb(234, 234, 234);
padding: 50px 0px; padding: 50px 0px;
height: 160px; height: 160px;
width: 49.5%; width: 49.5%;
float: right float: right;
} }
.avatar-uploader-icon { .avatar-uploader-icon {
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
font-size: 28px; font-size: 28px;
color: #8c939d; color: #8c939d;
width: 100px !important; width: 100px !important;
height: 100px !important; height: 100px !important;
line-height: 100px !important; line-height: 100px !important;
text-align: center; text-align: center;
} }
.material-img { .material-img {
width: 100%; width: 100%;
} }
.thumb-div { .thumb-div {
display: inline-block; display: inline-block;
text-align: center; text-align: center;
} }
.item-infos { .item-infos {
width: 30%; width: 30%;
margin: auto margin: auto;
} }
</style> </style>

View File

@ -2,7 +2,7 @@ import { createI18n } from 'vue-i18n';
import pinia from '/@/stores/index'; import pinia from '/@/stores/index';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { info } from '/@/api/admin/i18n' import { info } from '/@/api/admin/i18n';
// 定义语言国际化内容 // 定义语言国际化内容
@ -20,7 +20,7 @@ import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
// 定义变量内容 // 定义变量内容
const messages = {}; const messages = {};
const element = { en: enLocale, 'zh-cn': zhcnLocale}; const element = { en: enLocale, 'zh-cn': zhcnLocale };
const itemize = { en: [] as any[], 'zh-cn': [] as any[] }; const itemize = { en: [] as any[], 'zh-cn': [] as any[] };
const modules: Record<string, any> = import.meta.glob('./**/*.ts', { eager: true }); const modules: Record<string, any> = import.meta.glob('./**/*.ts', { eager: true });
const pages: Record<string, any> = import.meta.glob('./../../**/**/**/i18n/*.ts', { eager: true }); const pages: Record<string, any> = import.meta.glob('./../../**/**/**/i18n/*.ts', { eager: true });
@ -46,12 +46,14 @@ function mergeArrObj<T>(list: T, key: string) {
return obj; return obj;
} }
// 远程获取i18n // 远程获取i18n
const infoI18n = await info() try {
const infoI18n = await info();
itemize["zh-cn"].push(...infoI18n.data['zh-cn']) itemize['zh-cn'].push(...infoI18n.data['zh-cn']);
itemize.en.push(...infoI18n.data.en) itemize.en.push(...infoI18n.data.en);
} catch (e) {
// 考虑请求不过去没有后台的情况下导致的i18n失效
}
for (const key in itemize) { for (const key in itemize) {
messages[key] = { messages[key] = {
@ -61,7 +63,6 @@ for (const key in itemize) {
}; };
} }
// 读取 pinia 默认语言 // 读取 pinia 默认语言
const stores = useThemeConfig(pinia); const stores = useThemeConfig(pinia);
const { themeConfig } = storeToRefs(stores); const { themeConfig } = storeToRefs(stores);

View File

@ -8,10 +8,10 @@ import { useThemeConfig } from '/@/stores/themeConfig';
import { i18n } from '/@/i18n/index'; import { i18n } from '/@/i18n/index';
import { Local } from '/@/utils/storage'; import { Local } from '/@/utils/storage';
import { verifyUrl } from '/@/utils/toolsValidate'; import { verifyUrl } from '/@/utils/toolsValidate';
import request from "/@/utils/request"; import request from '/@/utils/request';
import { useMessage } from "/@/hooks/message"; import { useMessage } from '/@/hooks/message';
// @ts-ignore // @ts-ignore
import * as CryptoJS from "crypto-js"; import * as CryptoJS from 'crypto-js';
// 引入组件 // 引入组件
const SvgIcon = defineAsyncComponent(() => import('/@/components/svgIcon/index.vue')); const SvgIcon = defineAsyncComponent(() => import('/@/components/svgIcon/index.vue'));
@ -57,8 +57,10 @@ export function useTitle() {
export function setTagsViewNameI18n(item: any) { export function setTagsViewNameI18n(item: any) {
let tagsViewName: string = ''; let tagsViewName: string = '';
const { query, params, meta } = item; const { query, params, meta } = item;
//修复tagsViewName匹配到其他含下列单词的路由
const pattern = /^\{("(zh-cn|en|zh-tw)":"[^,]+",?){1,3}}$/;
if (query?.tagsViewName || params?.tagsViewName) { if (query?.tagsViewName || params?.tagsViewName) {
if (/\/zh-cn|en|zh-tw\//.test(query?.tagsViewName) || /\/zh-cn|en|zh-tw\//.test(params?.tagsViewName)) { if (pattern.test(query?.tagsViewName) || pattern.test(params?.tagsViewName)) {
// 国际化 // 国际化
const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName)); const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
tagsViewName = urlTagsParams[i18n.global.locale.value]; tagsViewName = urlTagsParams[i18n.global.locale.value];
@ -180,35 +182,29 @@ export function handleOpenLink(val: RouteItem) {
*/ */
export const openWindow = (url: string, title: string, w: number, h: number) => { export const openWindow = (url: string, title: string, w: number, h: number) => {
// Fixes dual-screen position Most browsers Firefox // Fixes dual-screen position Most browsers Firefox
const dualScreenLeft = const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
window.screenLeft !== undefined ? window.screenLeft : screen.left; const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
const dualScreenTop =
window.screenTop !== undefined ? window.screenTop : screen.top;
const width = window.innerWidth const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
? window.innerWidth
: document.documentElement.clientWidth
? document.documentElement.clientWidth
: screen.width;
const height = window.innerHeight const height = window.innerHeight
? window.innerHeight ? window.innerHeight
: document.documentElement.clientHeight : document.documentElement.clientHeight
? document.documentElement.clientHeight ? document.documentElement.clientHeight
: screen.height; : screen.height;
const left = width / 2 - w / 2 + dualScreenLeft; const left = width / 2 - w / 2 + dualScreenLeft;
const top = height / 2 - h / 2 + dualScreenTop; const top = height / 2 - h / 2 + dualScreenTop;
return window.open( return window.open(
url, url,
title, title,
"toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=" + 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' +
w + w +
", height=" + ', height=' +
h + h +
", top=" + ', top=' +
top + top +
", left=" + ', left=' +
left left
); );
}; };
/** /**
@ -217,7 +213,7 @@ export const openWindow = (url: string, title: string, w: number, h: number) =>
export function encryption(params: any) { export function encryption(params: any) {
let { data, type, param, key } = params; let { data, type, param, key } = params;
const result = JSON.parse(JSON.stringify(data)); const result = JSON.parse(JSON.stringify(data));
if (type === "Base64") { if (type === 'Base64') {
param.forEach((ele: any) => { param.forEach((ele: any) => {
result[ele] = btoa(result[ele]); result[ele] = btoa(result[ele]);
}); });
@ -230,7 +226,7 @@ export function encryption(params: any) {
var encrypted = CryptoJS.AES.encrypt(data, key, { var encrypted = CryptoJS.AES.encrypt(data, key, {
iv: iv, iv: iv,
mode: CryptoJS.mode.CFB, mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding padding: CryptoJS.pad.NoPadding,
}); });
result[ele] = encrypted.toString(); result[ele] = encrypted.toString();
}); });
@ -248,27 +244,27 @@ export function encryption(params: any) {
export function downBlobFile(url: any, query: any, fileName: string) { export function downBlobFile(url: any, query: any, fileName: string) {
return request({ return request({
url: url, url: url,
method: "get", method: 'get',
responseType: "blob", responseType: 'blob',
params: query params: query,
}).then(response => { }).then((response) => {
handleBlobFile(response, fileName) handleBlobFile(response, fileName);
}); });
} }
/** /**
* blob * blob
* @param response * @param response
* @returns * @returns
*/ */
export function handleBlobFile(response: any, fileName: string) { export function handleBlobFile(response: any, fileName: string) {
// 处理返回的文件流 // 处理返回的文件流
const blob = response; const blob = response;
if (blob && blob.size === 0) { if (blob && blob.size === 0) {
useMessage().error("内容为空,无法下载"); useMessage().error('内容为空,无法下载');
return; return;
} }
const link = document.createElement("a"); const link = document.createElement('a');
// 兼容一下 入参不是 File Blob 类型情况 // 兼容一下 入参不是 File Blob 类型情况
var binaryData = [] as any; var binaryData = [] as any;
@ -325,37 +321,37 @@ const other = {
handleOpenLink(val); handleOpenLink(val);
}, },
encryption: (data: any) => { encryption: (data: any) => {
return encryption(data) return encryption(data);
}, },
downBlobFile: (url: any, query: any, fileName: string) => { downBlobFile: (url: any, query: any, fileName: string) => {
return downBlobFile(url, query, fileName) return downBlobFile(url, query, fileName);
}, },
toUnderline: (str: string) => { toUnderline: (str: string) => {
return toUnderline(str) return toUnderline(str);
}, },
openWindow: (url: string, title: string, w: number, h: number) => { openWindow: (url: string, title: string, w: number, h: number) => {
return openWindow(url, title, w, h) return openWindow(url, title, w, h);
}, },
getQueryString: (url: string, paraName: string) => { getQueryString: (url: string, paraName: string) => {
return getQueryString(url, paraName) return getQueryString(url, paraName);
} },
}; };
export function getQueryString(url: string, paraName: string) { export function getQueryString(url: string, paraName: string) {
const arrObj = url.split("?"); const arrObj = url.split('?');
if (arrObj.length > 1) { if (arrObj.length > 1) {
const arrPara = arrObj[1].split("&"); const arrPara = arrObj[1].split('&');
let arr; let arr;
for (let i = 0; i < arrPara.length; i++) { for (let i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split("="); arr = arrPara[i].split('=');
// eslint-disable-next-line eqeqeq // eslint-disable-next-line eqeqeq
if (arr != null && arr[0] == paraName) { if (arr != null && arr[0] == paraName) {
return arr[1]; return arr[1];
} }
} }
return ""; return '';
} else { } else {
return ""; return '';
} }
} }
@ -369,25 +365,31 @@ export function getQueryString(url: string, paraName: string) {
* @returns {*} * @returns {*}
*/ */
export function handleTree(data, id, parentId, children, rootId) { export function handleTree(data, id, parentId, children, rootId) {
id = id || 'id' id = id || 'id';
parentId = parentId || 'parentId' parentId = parentId || 'parentId';
children = children || 'children' children = children || 'children';
rootId = rootId || Math.min.apply(Math, data.map(item => { rootId =
return item[parentId] rootId ||
})) || 0 Math.min.apply(
Math,
data.map((item) => {
return item[parentId];
})
) ||
0;
//对源数据深度克隆 //对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data)) const cloneData = JSON.parse(JSON.stringify(data));
//循环所有项 //循环所有项
const treeData = cloneData.filter(father => { const treeData = cloneData.filter((father) => {
const branchArr = cloneData.filter(child => { const branchArr = cloneData.filter((child) => {
//返回每一项的子级数组 //返回每一项的子级数组
return father[id] === child[parentId] return father[id] === child[parentId];
}) });
branchArr.length > 0 ? father[children] = branchArr : '' branchArr.length > 0 ? (father[children] = branchArr) : '';
//返回第一层 //返回第一层
return father[parentId] === rootId return father[parentId] === rootId;
}) });
return treeData !== '' ? treeData : data return treeData !== '' ? treeData : data;
} }
/** /**
@ -396,9 +398,8 @@ export function handleTree(data, id, parentId, children, rootId) {
* @returns 线 * @returns 线
*/ */
export function toUnderline(str: string) { export function toUnderline(str: string) {
return str.replace(/([A-Z])/g, "_$1").toLowerCase() return str.replace(/([A-Z])/g, '_$1').toLowerCase();
} }
// 统一批量导出 // 统一批量导出
export default other; export default other;

View File

@ -5,68 +5,70 @@ import qs from 'qs';
// 配置新建一个 axios 实例 // 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({ const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL, baseURL: import.meta.env.VITE_API_URL,
timeout: 50000, timeout: 5000,
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
paramsSerializer: { paramsSerializer: {
serialize(params) { serialize(params) {
return qs.stringify(params, { allowDots: true }); return qs.stringify(params, { allowDots: true });
}, },
}, },
}); });
// 添加请求拦截器 // 添加请求拦截器
service.interceptors.request.use((config: InternalAxiosRequestConfig) => { service.interceptors.request.use(
// get查询参数序列化 (config: InternalAxiosRequestConfig) => {
if (config.method === 'get') { // get查询参数序列化
// @ts-ignore if (config.method === 'get') {
config.paramsSerializer = (params: any) => { // @ts-ignore
return qs.stringify(params, { arrayFormat: 'repeat' }) config.paramsSerializer = (params: any) => {
} return qs.stringify(params, { arrayFormat: 'repeat' });
} };
// 统一增加 token }
const isToken = (config.headers || {}).isToken === false // 统一增加 token
if (Session.get('token') && !isToken) { const isToken = (config.headers || {}).isToken === false;
config.headers!['Authorization'] = `Bearer ${Session.get('token')}`; if (Session.get('token') && !isToken) {
} config.headers!['Authorization'] = `Bearer ${Session.get('token')}`;
}
// 统一增加租户信息 // 统一增加租户信息
if (Local.get('tenantId')) { if (Local.get('tenantId')) {
config.headers!['TENANT-ID'] = Local.get('tenantId'); config.headers!['TENANT-ID'] = Local.get('tenantId');
} }
return config; return config;
}, },
(error) => { (error) => {
// 对请求错误做些什么 // 对请求错误做些什么
return Promise.reject(error); return Promise.reject(error);
} }
); );
// 添加响应拦截器 // 添加响应拦截器
service.interceptors.response.use((res: any) => { service.interceptors.response.use(
if (res.data.code === 1) { (res: any) => {
throw res.data if (res.data.code === 1) {
} throw res.data;
return res.data; }
}, error => { return res.data;
const status = Number(error.response.status) || 200 },
if (status === 424) { (error) => {
ElMessageBox.confirm('令牌状态已过期,请点击重新登录', '系统提示', { console.log(error, 'rrrrr');
confirmButtonText: '重新登录', const status = Number(error.response.status) || 200;
cancelButtonText: '取消', if (status === 424) {
type: 'warning' ElMessageBox.confirm('令牌状态已过期,请点击重新登录', '系统提示', {
}) confirmButtonText: '重新登录',
.then(() => { cancelButtonText: '取消',
Session.clear(); // 清除浏览器全部临时缓存 type: 'warning',
window.location.href = '/'; // 去登录页 }).then(() => {
return Session.clear(); // 清除浏览器全部临时缓存
}) window.location.href = '/'; // 去登录页
} return;
});
return Promise.reject(error.response.data); }
}) return Promise.reject(error.response.data);
}
);
// 导出 axios 实例 // 导出 axios 实例
export default service; export default service;

View File

@ -8,18 +8,23 @@ import Cookies from 'js-cookie';
* @method clear * @method clear
*/ */
export const Local = { export const Local = {
// 查看 v2.4.3版本更新日志
setKey(key: string) {
// @ts-ignore
return `${__NEXT_NAME__}:${key}`;
},
// 设置永久缓存 // 设置永久缓存
set(key: string, val: any) { set<T>(key: string, val: T) {
window.localStorage.setItem(key, JSON.stringify(val)); window.localStorage.setItem(Local.setKey(key), JSON.stringify(val));
}, },
// 获取永久缓存 // 获取永久缓存
get(key: string) { get(key: string) {
let json = <string>window.localStorage.getItem(key); let json = <string>window.localStorage.getItem(Local.setKey(key));
return JSON.parse(json); return JSON.parse(json);
}, },
// 移除永久缓存 // 移除永久缓存
remove(key: string) { remove(key: string) {
window.localStorage.removeItem(key); window.localStorage.removeItem(Local.setKey(key));
}, },
// 移除全部永久缓存 // 移除全部永久缓存
clear() { clear() {

View File

@ -1,130 +1,124 @@
<template> <template>
<el-drawer v-model="visible" :title="$t('personal.name')" size="50%"> <el-drawer v-model="visible" :title="$t('personal.name')" size="50%">
<el-tabs style="height: 200px" class="demo-tabs"> <el-tabs style="height: 200px" class="demo-tabs">
<el-tab-pane label="基本信息"> <el-tab-pane label="基本信息">
<el-card shadow="hover" class="layout-padding-auto"> <el-card shadow="hover" class="layout-padding-auto">
<el-form :model="formData" :rules="ruleForm" label-width="100px" class="mt35 mb35" ref="formdataRef"> <el-form :model="formData" :rules="ruleForm" label-width="100px" class="mt35 mb35" ref="formdataRef">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="用户名" prop="avatar"> <el-form-item label="用户名" prop="avatar">
<image-upload class="h100 personal-user-left-upload" @change="handleAvatarSuccess"> <image-upload class="h100 personal-user-left-upload" @change="handleAvatarSuccess">
<img v-if="formData.avatar" :src="formData.avatar" class="avatar" /> <img v-if="formData.avatar" :src="formData.avatar" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"> <el-icon v-else class="avatar-uploader-icon">
<Plus /> <Plus />
</el-icon> </el-icon>
</image-upload> </image-upload>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="用户名" prop="username"> <el-form-item label="用户名" prop="username">
<el-input v-model="formData.username" clearable disabled></el-input> <el-input v-model="formData.username" clearable disabled></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="手机" prop="phone"> <el-form-item label="手机" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入手机" clearable></el-input> <el-input v-model="formData.phone" placeholder="请输入手机" clearable></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" clearable></el-input> <el-input v-model="formData.email" placeholder="请输入邮箱" clearable></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="昵称" prop="nickname"> <el-form-item label="昵称" prop="nickname">
<el-input v-model="formData.nickname" placeholder="请输入昵称" clearable></el-input> <el-input v-model="formData.nickname" placeholder="请输入昵称" clearable></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="姓名" prop="name"> <el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" clearable></el-input> <el-input v-model="formData.name" placeholder="请输入姓名" clearable></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="社交登录" prop="social" > <el-form-item label="社交登录" prop="social">
<a-row style="display: flex;justify-content: space-between;width: 300px"> <el-row style="display: flex; justify-content: space-between; width: 300px">
<div @click="handleClick('wechat')" class="item"> <div @click="handleClick('wechat')" class="item">
<SvgIcon name="local-wechat" :size="20"/> <SvgIcon name="local-wechat" :size="20" />
<p class="title">微信</p> <p class="title">微信</p>
</div> </div>
<div @click="handleClick('tencent')" class="item"> <div @click="handleClick('tencent')" class="item">
<SvgIcon name="local-qq" :size="20"/> <SvgIcon name="local-qq" :size="20" />
<p class="title">QQ</p> <p class="title">QQ</p>
</div> </div>
<div @click="handleClick('gitee')" class="item"> <div @click="handleClick('gitee')" class="item">
<SvgIcon name="local-gitee" :size="20"/> <SvgIcon name="local-gitee" :size="20" />
<p class="title">Gitee</p> <p class="title">Gitee</p>
</div> </div>
<div @click="handleClick('osc')" class="item"> <div @click="handleClick('osc')" class="item">
<SvgIcon name="local-oschina" :size="20"/> <SvgIcon name="local-oschina" :size="20" />
<p class="title">开源中国</p> <p class="title">开源中国</p>
</div> </div>
</a-row> </el-row>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleSaveUser"> <el-button type="primary" @click="handleSaveUser"> 更新个人信息 </el-button>
更新个人信息 </el-form-item>
</el-button> </el-col>
</el-form-item> </el-row>
</el-col> </el-form>
</el-row> </el-card>
</el-form> </el-tab-pane>
</el-card> <el-tab-pane label="安全信息">
</el-tab-pane> <el-card shadow="hover" class="layout-padding-auto">
<el-tab-pane label="安全信息"> <el-form :model="passwordFormData" :rules="passwordRuleForm" label-width="100px" class="mt35 mb35" ref="passwordFormdataRef">
<el-card shadow="hover" class="layout-padding-auto"> <el-row :gutter="20">
<el-form :model="passwordFormData" :rules="passwordRuleForm" label-width="100px" class="mt35 mb35" ref="passwordFormdataRef"> <el-col :span="24" class="mb20">
<el-row :gutter="20"> <el-form-item label="原密码" prop="password">
<el-col :span="24" class="mb20"> <el-input v-model="passwordFormData.password" placeholder="请输入密码" clearable type="password"></el-input>
<el-form-item label="原密码" prop="password"> </el-form-item>
<el-input v-model="passwordFormData.password" placeholder="请输入密码" clearable </el-col>
type="password"></el-input> <el-col :span="24" class="mb20">
</el-form-item> <el-form-item label="新密码" prop="newpassword1">
</el-col> <el-input v-model="passwordFormData.newpassword1" clearable type="password"></el-input>
<el-col :span="24" class="mb20"> </el-form-item>
<el-form-item label="新密码" prop="newpassword1"> </el-col>
<el-input v-model="passwordFormData.newpassword1" clearable type="password"></el-input> <el-col :span="24" class="mb20">
</el-form-item> <el-form-item label="确认密码" prop="newpassword2">
</el-col> <el-input v-model="passwordFormData.newpassword2" clearable type="password"></el-input>
<el-col :span="24" class="mb20"> </el-form-item>
<el-form-item label="确认密码" prop="newpassword2"> </el-col>
<el-input v-model="passwordFormData.newpassword2" clearable type="password"></el-input> <el-col :span="24" class="mb20">
</el-form-item> <el-form-item>
</el-col> <el-button type="primary" @click="handleChangePassword"> 修改密码 </el-button>
<el-col :span="24" class="mb20"> </el-form-item>
<el-form-item> </el-col>
<el-button type="primary" @click="handleChangePassword"> </el-row>
修改密码 </el-form>
</el-button> </el-card>
</el-form-item> </el-tab-pane>
</el-col> </el-tabs>
</el-row> </el-drawer>
</el-form>
</el-card>
</el-tab-pane>
</el-tabs>
</el-drawer>
</template> </template>
<script setup lang="ts" name="personal"> <script setup lang="ts" name="personal">
import { useUserInfo } from '/@/stores/userInfo'; import { useUserInfo } from '/@/stores/userInfo';
import { editInfo, password } from '/@/api/admin/user' import { editInfo, password } from '/@/api/admin/user';
import { useMessage } from "/@/hooks/message"; import { useMessage } from '/@/hooks/message';
import { rule } from "/@/utils/validate"; import { rule } from '/@/utils/validate';
import other from '/@/utils/other'; import other from '/@/utils/other';
import {Session} from "/@/utils/storage"; import { Session } from '/@/utils/storage';
import { useI18n } from "vue-i18n"; import { useI18n } from 'vue-i18n';
const { t } = useI18n() const { t } = useI18n();
const ImageUpload = defineAsyncComponent(() => import('/@/components/Upload/Image.vue'));
const ImageUpload = defineAsyncComponent(() => import('/@/components/Upload/Image.vue')) const visible = ref(false);
const visible = ref(false)
// //
const formData = reactive({ const formData = reactive({
@ -137,122 +131,124 @@ const formData = reactive({
}); });
const passwordFormData = reactive({ const passwordFormData = reactive({
password: '', password: '',
newpassword1: '', newpassword1: '',
newpassword2: '' newpassword2: '',
}) });
const formdataRef = ref() const formdataRef = ref();
const passwordFormdataRef = ref() const passwordFormdataRef = ref();
const ruleForm = reactive({ const ruleForm = reactive({
phone: [
phone: [{ required: true, message: "手机号不能为空", trigger: "blur" }, { validator: rule.validatePhone, trigger: 'blur' }], { required: true, message: '手机号不能为空', trigger: 'blur' },
nickname: [{ required: true, message: "昵称不能为空", trigger: "blur" }], { validator: rule.validatePhone, trigger: 'blur' },
email: [{ required: true, message: "邮箱不能为空", trigger: "blur" }], ],
name: [{ required: true, message: "姓名不能为空", trigger: "blur" }], nickname: [{ required: true, message: '昵称不能为空', trigger: 'blur' }],
email: [{ required: true, message: '邮箱不能为空', trigger: 'blur' }],
}) name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
const validatorPassword2 = (rule: any, value: any, callback: any) => { });
if(value !== passwordFormData.newpassword1){ const validatorPassword2 = (rule: any, value: any, callback: any) => {
callback(new Error(t('personal.passwordRule'))) if (value !== passwordFormData.newpassword1) {
}else{ callback(new Error(t('personal.passwordRule')));
callback() } else {
} callback();
} }
};
const passwordRuleForm = reactive({ const passwordRuleForm = reactive({
password: [{ required: true, message: "密码不能为空", trigger: "blur" }], password: [{ required: true, message: '密码不能为空', trigger: 'blur' }],
newpassword1: [{ newpassword1: [
min: 6, {
max: 20, min: 6,
message: "用户密码长度必须介于 6 和 20 之间", max: 20,
trigger: "blur" message: '用户密码长度必须介于 6 和 20 之间',
}], trigger: 'blur',
newpassword2: [ },
{ ],
min: 6, newpassword2: [
max: 20, {
message: "用户密码长度必须介于 6 和 20 之间", min: 6,
trigger: "blur" max: 20,
},{ validator: validatorPassword2, trigger: 'blur' } message: '用户密码长度必须介于 6 和 20 之间',
] trigger: 'blur',
}) },
{ validator: validatorPassword2, trigger: 'blur' },
],
});
// //
const handleAvatarSuccess = (url: any) => { const handleAvatarSuccess = (url: any) => {
formData.avatar = url; formData.avatar = url;
} };
const handleChangePassword = () => { const handleChangePassword = () => {
passwordFormdataRef.value.validate((valid: boolean) => { passwordFormdataRef.value.validate((valid: boolean) => {
if (!valid) { if (!valid) {
return false return false;
} }
password(passwordFormData).then(() => { password(passwordFormData)
useMessage().success("修改成功") .then(() => {
// useMessage().success('修改成功');
// /token //
Session.clear(); // /token
// 使 reload resetRoute() Session.clear();
window.location.reload(); // 使 reload resetRoute()
}).catch(err => { window.location.reload();
useMessage().error(err.msg) })
}) .catch((err) => {
}) useMessage().error(err.msg);
} });
});
};
// //
const handleSaveUser = () => { const handleSaveUser = () => {
formdataRef.value.validate((valid: boolean) => { formdataRef.value.validate((valid: boolean) => {
if (!valid) { if (!valid) {
return false return false;
} }
editInfo(formData).then(() => { editInfo(formData)
useMessage().success("修改成功") .then(() => {
// user useMessage().success('修改成功');
useUserInfo().setUserInfos() // user
}).catch(err => { useUserInfo().setUserInfos();
useMessage().error(err.msg) })
}) .catch((err) => {
}) useMessage().error(err.msg);
});
} });
};
const handleClick = (thirdpart: string) => { const handleClick = (thirdpart: string) => {
let appid, client_id, redirect_uri, url; let appid, client_id, redirect_uri, url;
redirect_uri = encodeURIComponent( redirect_uri = encodeURIComponent(window.location.origin + '/#/authredirect');
window.location.origin + "/#/authredirect" if (thirdpart === 'wechat') {
); appid = 'wxd1678d3f83b1d83a';
if (thirdpart === "wechat") {
appid = "wxd1678d3f83b1d83a";
url = `https://open.weixin.qq.com/connect/qrconnect?appid=${appid}&redirect_uri=${redirect_uri}&state=WX-BIND&response_type=code&scope=snsapi_login#wechat_redirect`; url = `https://open.weixin.qq.com/connect/qrconnect?appid=${appid}&redirect_uri=${redirect_uri}&state=WX-BIND&response_type=code&scope=snsapi_login#wechat_redirect`;
} else if (thirdpart === "tencent") { } else if (thirdpart === 'tencent') {
client_id = "101322838"; client_id = '101322838';
url = `https://graph.qq.com/oauth2.0/authorize?response_type=code&state=QQ-BIND&client_id=${client_id}&redirect_uri=${redirect_uri}`; url = `https://graph.qq.com/oauth2.0/authorize?response_type=code&state=QQ-BIND&client_id=${client_id}&redirect_uri=${redirect_uri}`;
} else if (thirdpart === "gitee") { } else if (thirdpart === 'gitee') {
client_id = '0c29cfd9cb1e0037fc837521bc08c1a7483d8fd9b3e123d46beec59a5544a881' client_id = '0c29cfd9cb1e0037fc837521bc08c1a7483d8fd9b3e123d46beec59a5544a881';
url = `https://gitee.com/oauth/authorize?response_type=code&state=GITEE-BIND&client_id=${client_id}&redirect_uri=${redirect_uri}`; url = `https://gitee.com/oauth/authorize?response_type=code&state=GITEE-BIND&client_id=${client_id}&redirect_uri=${redirect_uri}`;
} else if (thirdpart === "osc") { } else if (thirdpart === 'osc') {
client_id = "neIIqlwGsjsfsA6uxNqD"; client_id = 'neIIqlwGsjsfsA6uxNqD';
url = `https://www.oschina.net/action/oauth2/authorize?response_type=code&client_id=${client_id}&state=OSC-BIND&redirect_uri=${redirect_uri}`; url = `https://www.oschina.net/action/oauth2/authorize?response_type=code&client_id=${client_id}&state=OSC-BIND&redirect_uri=${redirect_uri}`;
} }
other.openWindow(url, thirdpart, 540, 540) other.openWindow(url, thirdpart, 540, 540);
} };
const open = () => { const open = () => {
visible.value = true visible.value = true;
const data = useUserInfo().userInfos const data = useUserInfo().userInfos;
Object.assign(formData, data.user) Object.assign(formData, data.user);
} };
// //
defineExpose({ defineExpose({
open, open,
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -461,9 +457,9 @@ defineExpose({
height: 100%; height: 100%;
} }
.item{ .item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
} }
</style> </style>

View File

@ -90,6 +90,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
__VUE_I18N_FULL_INSTALL__: JSON.stringify(false), __VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false), __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
__VERSION__: JSON.stringify(process.env.npm_package_version), __VERSION__: JSON.stringify(process.env.npm_package_version),
__NEXT_NAME__: JSON.stringify(process.env.npm_package_name),
}, },
}; };
}); });