后台前端

This commit is contained in:
Double-_-Z 2024-10-31 09:56:55 +08:00
parent aec7b928f5
commit 5a4d9181cb
20 changed files with 1323 additions and 4224 deletions

2
.env
View File

@ -8,4 +8,4 @@ VITE_OPEN = false
VITE_OPEN_CDN = false VITE_OPEN_CDN = false
# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可 # public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
VITE_PUBLIC_PATH = /vue-next-admin-preview/ VITE_PUBLIC_PATH = ./

View File

@ -2,4 +2,5 @@
ENV = production ENV = production
# 线上环境接口地址 # 线上环境接口地址
VITE_API_URL = https://lyt-top.gitee.io/vue-next-admin-preview/ # VITE_API_URL = https://lyt-top.gitee.io/vue-next-admin-preview/
VITE_API_URL = http://8.138.171.103/

4694
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@
"@typescript-eslint/parser": "^7.2.0", "@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/compiler-sfc": "^3.4.21", "@vue/compiler-sfc": "^3.4.21",
"code-inspector-plugin": "^0.17.6",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0", "eslint-plugin-vue": "^9.23.0",
"prettier": "^3.2.5", "prettier": "^3.2.5",

50
src/api/article/index.ts Normal file
View File

@ -0,0 +1,50 @@
import request from '/@/utils/request';
import { baseUrlHost } from '../baseUrlHost';
/**
* request.post(xxx) post params data
* get请求时paramsdata
*
* api接口集合
* @method getArticleList
* @method getModuleList
* @method getLabelList
* @method deleteArticle
* @method getArticleDetail
*/
export function articleApi() {
return {
getArticleList: (params: object) => {
return request({
url: baseUrlHost + '/cpArticle',
method: 'get',
params,
});
},
getModuleList: () => {
return request({
url: baseUrlHost + '/cpModule/all',
method: 'get',
});
},
getLabelList: (moduleid: Number) => {
return request({
url: baseUrlHost + `/cpLabel/moduleid/${moduleid}`,
method: 'get',
});
},
deleteArticle: (id: Number) => {
return request({
url: baseUrlHost + `/cpArticle/${id}`,
method: 'delete',
});
},
getArticleDetail: (id: Number) => {
return request({
url: baseUrlHost + `/cpArticle/${id}`,
method: 'get',
});
}
};
}

1
src/api/baseUrlHost.ts Normal file
View File

@ -0,0 +1 @@
export const baseUrlHost = "/vueAdminApi";

View File

@ -1,12 +1,16 @@
import request from '/@/utils/request'; import request from '/@/utils/request';
import { baseUrlHost } from '../baseUrlHost';
/** /**
* request.post(xxx) post params data * request.post(xxx) post params data
* * get请求时paramsdata
* api接口集合 * api接口集合
* @method signIn * @method signIn
* @method signOut 退 * @method signOut 退
* @method login
* @method loginOut
*/ */
export function useLoginApi() { export function useLoginApi() {
return { return {
signIn: (data: object) => { signIn: (data: object) => {
@ -23,5 +27,18 @@ export function useLoginApi() {
data, data,
}); });
}, },
login: (data: object) => {
return request({
url: baseUrlHost+'/acUser/login',
method: 'post',
data,
});
},
loginOut: () => {
return request({
url: baseUrlHost+'/acUser/logout',
method: 'get',
});
}
}; };
} }

View File

@ -19,6 +19,7 @@
:prop="item.key" :prop="item.key"
:width="item.colWidth" :width="item.colWidth"
:label="item.title" :label="item.title"
:formatter="item.formatter"
> >
<template v-slot="scope"> <template v-slot="scope">
<template v-if="item.type === 'image'"> <template v-if="item.type === 'image'">

View File

@ -8,6 +8,10 @@ export default {
systemUser: 'systemUser', systemUser: 'systemUser',
systemDept: 'systemDept', systemDept: 'systemDept',
systemDic: 'systemDic', systemDic: 'systemDic',
article: 'article',
articleDetail: 'articleDetail',
addArticle: 'addArticle',
editArticle: 'editArticle',
limits: 'limits', limits: 'limits',
limitsFrontEnd: 'FrontEnd', limitsFrontEnd: 'FrontEnd',
limitsFrontEndPage: 'FrontEndPage', limitsFrontEndPage: 'FrontEndPage',

View File

@ -8,6 +8,10 @@ export default {
systemUser: '用户管理', systemUser: '用户管理',
systemDept: '部门管理', systemDept: '部门管理',
systemDic: '字典管理', systemDic: '字典管理',
article: '文章管理',
articleDetail: '文章详情',
addArticle: '新增文章',
editArticle: '编辑文章',
limits: '权限管理', limits: '权限管理',
limitsFrontEnd: '前端控制', limitsFrontEnd: '前端控制',
limitsFrontEndPage: '页面权限', limitsFrontEndPage: '页面权限',

View File

@ -8,6 +8,10 @@ export default {
systemUser: '用戶管理', systemUser: '用戶管理',
systemDept: '部門管理', systemDept: '部門管理',
systemDic: '字典管理', systemDic: '字典管理',
article: '文章管理',
articleDetail: '文章詳情',
addArticle: '文章新增',
editArticle: '文章編輯',
limits: '許可權管理', limits: '許可權管理',
limitsFrontEnd: '前端控制', limitsFrontEnd: '前端控制',
limitsFrontEndPage: '頁面許可權', limitsFrontEndPage: '頁面許可權',

View File

@ -97,11 +97,15 @@ import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other'; import other from '/@/utils/other';
import mittBus from '/@/utils/mitt'; import mittBus from '/@/utils/mitt';
import { Session, Local } from '/@/utils/storage'; import { Session, Local } from '/@/utils/storage';
import { useLoginApi } from '/@/api/login';
// //
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue')); const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/topBar/userNews.vue'));
const Search = defineAsyncComponent(() => import('/@/layout/navBars/topBar/search.vue')); const Search = defineAsyncComponent(() => import('/@/layout/navBars/topBar/search.vue'));
// api
const loginApi = useLoginApi();
// //
const userNewsRef = ref(); const userNewsRef = ref();
const userNewsBadgeRef = ref(); const userNewsBadgeRef = ref();
@ -159,16 +163,17 @@ const onHandleCommandClick = (path: string) => {
confirmButtonText: t('message.user.logOutConfirm'), confirmButtonText: t('message.user.logOutConfirm'),
cancelButtonText: t('message.user.logOutCancel'), cancelButtonText: t('message.user.logOutCancel'),
buttonSize: 'default', buttonSize: 'default',
beforeClose: (action, instance, done) => { beforeClose: async (action, instance, done) => {
if (action === 'confirm') { if (action === 'confirm') {
instance.confirmButtonLoading = true; try {
instance.confirmButtonText = t('message.user.logOutExit'); instance.confirmButtonLoading = true;
setTimeout(() => { instance.confirmButtonText = t('message.user.logOutExit');
await loginApi.loginOut();
} catch (error) {
} finally {
done(); done();
setTimeout(() => { instance.confirmButtonLoading = false;
instance.confirmButtonLoading = false; }
}, 300);
}, 700);
} else { } else {
done(); done();
} }
@ -176,6 +181,7 @@ const onHandleCommandClick = (path: string) => {
}) })
.then(async () => { .then(async () => {
// /token // /token
console.log('清除缓存/token等');
Session.clear(); Session.clear();
// 使 reload resetRoute() // 使 reload resetRoute()
window.location.reload(); window.location.reload();

View File

@ -155,6 +155,68 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
}, },
], ],
}, },
{
path: '/article',
name: 'article',
component: () => import('/@/views/article/index.vue'),
meta: {
title: 'message.router.article',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
isIframe: false,
roles: ['admin', 'common'],
icon: 'iconfont icon-jiliandongxuanzeqi',
},
children: [
{
path: '/article/detail',
name: 'articleDetail',
component: () => import('/@/views/article/component/detail.vue'),
meta: {
title: 'message.router.articleDetail',
isLink: '',
isHide: true,
isKeepAlive: false,
isAffix: false,
isIframe: false,
roles: ['admin','common'],
icon: 'ele-Document',
},
},
{
path: '/article/add',
name: 'addArticle',
component: () => import('/@/views/article/component/upload.vue'),
meta: {
title: 'message.router.addArticle',
isLink: '',
isHide: true,
isKeepAlive: false,
isAffix: false,
isIframe: false,
roles: ['admin','common'],
icon: 'ele-Document',
},
},
{
path: '/article/edit',
name: 'editArticle',
component: () => import('/@/views/article/component/upload.vue'),
meta: {
title: 'message.router.editArticle',
isLink: '',
isHide: true,
isKeepAlive: false,
isAffix: false,
isIframe: false,
roles: ['admin','common'],
icon: 'ele-Document',
},
}
]
},
{ {
path: '/limits', path: '/limits',
name: 'limits', name: 'limits',

View File

@ -21,6 +21,7 @@ service.interceptors.request.use(
// 在发送请求之前做些什么 token // 在发送请求之前做些什么 token
if (Session.get('token')) { if (Session.get('token')) {
config.headers!['Authorization'] = `${Session.get('token')}`; config.headers!['Authorization'] = `${Session.get('token')}`;
config.headers!['token'] = `${Session.get('token')}`;
} }
return config; return config;
}, },

View File

@ -0,0 +1,143 @@
<template>
<div class="layout-pd">
<el-card v-loading="state.loading" shadow="hover" header="文章详情">
<el-form :model="state.data" size="large" label-width="100px" class="mt20 mb20">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="封面:">
<el-image
style="width: 100px;height: 100px;border-radius: 4px;"
:src="state.data.photo"
:zoom-rate="1.2"
:preview-src-list="[state.data.photo]"
preview-teleported
fit="cover"/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="模块名称:">
{{ state.data.moduleName }}
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="标签名称:">
{{ state.data.labelName }}
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="标题:">
{{ state.data.title }}
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="文章类型:">
图文
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="是否置顶:">
<el-tag type="success" v-if="state.data.top===1"></el-tag>
<el-tag type="error" v-else></el-tag>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="浏览数:">
{{ state.data.view }}
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="创建日期:">
{{ dateFormatter() }}
</el-form-item>
</el-col>
</el-row>
<!-- <div class="mt5 mb20 ml10">文章内容</div> -->
<Editor v-model:get-html="state.data.text" v-model:get-text="state.data.text" :disable="true" />
</el-form>
</el-card>
</div>
</template>
<script setup lang="ts" name="articleDetail">
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
import { articleApi } from '/@/api/article';
import { useRoute } from 'vue-router';
import { ElMessage } from 'element-plus';
//
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
// api
const artApi = articleApi();
//
const moduleList = ref([]);
//
const route = useRoute();
const moduleName:any = route.query.moduleName;
const labelName:any = route.query.labelName;
const state = reactive({
data: {
labelName: '',
moduleName: '',
title: '',
articletype: 0,
createtime: '',
photo: '',
text: '',
top: 0,
view: 0,
},
loading: false,
})
//
const getArticleDetail = async() => {
try {
state.loading = true;
let res = await artApi.getArticleList({
moduleName: moduleName,
labelName: labelName,
size: 1,
current: 1
});
if(res?.success) {
state.data = res.data.records[0];
}
} catch (error) {
ElMessage.error('获取文章详情失败');
} finally {
state.loading = false;
}
}
//
const getModuleList = async() => {
try {
let res = await artApi.getModuleList();
if(res?.success){
moduleList.value = res.data;
}else{
ElMessage.error('模块列表获取失败!');
}
} catch (error) {
ElMessage.error('模块列表获取失败!');
} finally {
}
}
//
function dateFormatter(){
if(!state.data.createtime) return '';
let date = new Date(state.data.createtime);
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}
onMounted(() => {
getArticleDetail();
getModuleList();
})
</script>

View File

@ -0,0 +1,234 @@
<template>
<div class="layout-pd">
<el-card v-loading="state.loading" shadow="hover" header="文章详情">
<el-form ref="articleFormRef" :model="state.data" :rules="state.rules" size="large" label-width="100px" class="mt20 mb20">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="封面:" class="coverImg">
<!-- 上传文件 -->
<el-upload ref="uploadRef" class="h100 personal-user-left-upload" action="#" list-type="picture-card"
:auto-upload="false" :limit="1" :class="{ hide: state.coverHide }" @change="handleUploadChange">
<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="handleDownload(file)"
>
<el-icon><Download /></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="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="模块名称:" prop="moduleName">
<el-select size="default" v-model="state.data.moduleName" placeholder="请选择模块" clearable>
<el-option :data-op="item.id" v-for="(item,index) in moduleList" :key="index" :label="item.moduleName" :value="item.moduleName"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="标签名称:" prop="labelName">
<el-input v-model="state.data.labelName" placeholder="请输入标签名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="标题:" prop="title">
<el-input v-model="state.data.title" placeholder="请输入标题" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="文章类型:" prop="articletype">
<el-select size="default" v-model="state.data.articletype" placeholder="请选择文章类型" clearable>
<el-option label="图文" :value="0"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="是否置顶:" prop="top">
<el-switch v-model="state.data.top" />
</el-form-item>
</el-col>
</el-row>
<el-form-item prop="text" label-width="0px">
<Editor v-model:get-html="state.data.text" v-model:get-text="state.data.text" :disable="true" />
</el-form-item>
<el-row class="flex mt15">
<div class="flex-margin" style="width: 100%;display: flex;justify-content: flex-end;">
<el-button size="larger" type="primary" @click="onSubmitForm">
<SvgIcon name="iconfont icon-shuxing" />
保存
</el-button>
</div>
</el-row>
</el-form>
</el-card>
<!-- 封面预览弹窗 -->
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="预览" />
</el-dialog>
</div>
</template>
<script setup lang="ts" name="articleDetail">
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
import { articleApi } from '/@/api/article';
import { useRoute } from 'vue-router';
import { ElMessage, UploadFile, UploadFiles } from 'element-plus';
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
//
const Editor = defineAsyncComponent(() => import('/@/components/editor/index.vue'));
// api
const artApi = articleApi();
//
const moduleList = ref([]);
// id
const route = useRoute();
const moduleName:any = route.query.moduleName;
const labelName:any = route.query.labelName;
const state = reactive({
data: {
labelName: '',
moduleName: '',
title: '',
articletype: '',
photo: '',
text: '',
top: false,// 01
},
loading: false,
rules: {
moduleName: { required: true, message: '请选择模块', trigger: 'blur' },
labelName: { required: true, message: '请输入标签名称', trigger: 'blur' },
title: { required: true, message: '请输入标题', trigger: 'blur' },
articletype: { required: true, message: '请选择文章类型', trigger: 'blur' },
photo: { required: true, message: '请上传图片', trigger: 'blur' },
top: { required: true, message: '请选择是否置顶', trigger: 'blur' },
text: { required: true, message: '请输入文章内容', trigger: 'blur' },
},
coverFile: {},
coverHide: false
})
//
const getArticleDetail = async() => {
try {
state.loading = true;
let res = await artApi.getArticleList({
moduleName: moduleName,
labelName: labelName,
size: 1,
current: 1
});
if(res?.success) {
state.data = res.data.records[0];
// 01truefalse
// ts
state.data.top = state.data.top === 1;
}
} catch (error) {
ElMessage.error('获取文章详情失败');
} finally {
state.loading = false;
}
}
//
const getModuleList = async() => {
try {
let res = await artApi.getModuleList();
if(res?.success){
moduleList.value = res.data;
}else{
ElMessage.error('模块列表获取失败!');
}
} catch (error) {
ElMessage.error('模块列表获取失败!');
} finally {
}
}
const articleFormRef = ref();
//
const onSubmitForm = () => {
articleFormRef.value.validate((valid: boolean) => {
if (valid){
}
});
};
//
const disabled = ref(false);
//
const dialogImageUrl = ref('');
const dialogVisible = ref(false);
const uploadRef = ref();
//
const handleRemove = (file: UploadFile) => {
state.coverFile = {};
state.coverHide = false;
uploadRef.value.clearFiles();
}
//
const handlePictureCardPreview = (file: UploadFile) => {
dialogImageUrl.value = file.url!
dialogVisible.value = true
}
//
const handleDownload = (file: UploadFile) => {
console.log(file)
}
//
const handleUploadChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
const file:any = uploadFile.raw;
state.coverFile = file;
state.coverHide = true;
}
onMounted(() => {
if(moduleName && labelName && route.path == '/article/edit')
getArticleDetail();
getModuleList();
})
</script>
<style scoped lang="scss">
.coverImg :deep(.hide .el-upload--picture-card) {
display: none;
}
</style>

244
src/views/article/index.vue Normal file
View File

@ -0,0 +1,244 @@
<template>
<div class="system-dept-container layout-padding">
<el-card shadow="hover" class="layout-padding-auto">
<div class="system-dept-search mb15">
<el-select @change="handleChangeModule" size="default" v-model="state.tableData.param.moduleName" placeholder="请选择模块" clearable style="max-width: 180px">
<el-option :data-op="item.id" v-for="(item,index) in moduleList" :key="index" :label="item.moduleName" :value="item.moduleName"></el-option>
</el-select>
<el-select size="default" v-model="state.tableData.param.labelName" placeholder="请选择标签" clearable class="ml10" style="max-width: 180px">
<el-option v-for="(item,index) in labelList" :key="index" :label="item.name" :value="item.name"></el-option>
</el-select>
<el-input size="default" placeholder="请输入标题" v-model="state.tableData.param.title" class="ml10" style="max-width: 180px"> </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 @click="toAddArticle" size="default" type="success" 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 type="index" label="序号" width="100"/>
<el-table-column prop="moduleName" label="模块名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="labelName" label="标签名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="title" label="标题" show-overflow-tooltip></el-table-column>
<el-table-column prop="articletype" label="文章类型" :formatter="typeFormatter" show-overflow-tooltip></el-table-column>
<el-table-column label="置顶" show-overflow-tooltip>
<template #default="scope">
<el-tag type="success" v-if="scope.row.top===1"></el-tag>
<el-tag type="error" v-else></el-tag>
</template>
</el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" text type="primary" @click="toShowDetail(scope.row)">查看详情</el-button>
<el-button size="small" text type="primary" @click="toEditArticle(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="deleteArticle(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="onHandleSizeChange"
@current-change="onHandleCurrentChange"
class="mt15"
:pager-count="5"
:page-sizes="[10, 20, 30]"
v-model:current-page="state.tableData.param.current"
background
v-model:page-size="state.tableData.param.size"
layout="total, sizes, prev, pager, next, jumper"
:total="state.tableData.total"
>
</el-pagination>
</el-card>
<!-- <DeptDialog ref="deptDialogRef" @refresh="getTableData()" /> -->
</div>
</template>
<script setup lang="ts" name="articleIndex">
import { onMounted, reactive, ref } from 'vue';
import { articleApi } from '/@/api/article';
import { ElMessage, ElMessageBox, TableColumnCtx } from 'element-plus';
import { useRouter } from 'vue-router';
const router = useRouter();
//
const state = reactive({
tableData: {
data: [],
total: 0,
loading: false,
param: {
labelName: '',
moduleName: '',
title: '',
current: 1,
size: 10,
},
},
});
//
const moduleList = ref([]);
//
const labelList = ref([]);
// api
const artApi = articleApi();
//
const getTableData = async() => {
try {
state.tableData.loading = true;
let res = await artApi.getArticleList(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 getModuleList = async() => {
try {
let res = await artApi.getModuleList();
if(res?.success){
moduleList.value = res.data;
}else{
ElMessage.error('模块列表获取失败!');
}
} catch (error) {
ElMessage.error('模块列表获取失败!');
} finally {
}
}
//
const handleChangeModule = async(val: any) => {
try {
state.tableData.param.labelName = '';
if(!val){labelList.value=[]; return;}
const op:any = event?.currentTarget;
let res = await artApi.getLabelList(op.dataset.op);
if(res?.success){
labelList.value = res.data;
}else{
ElMessage.error('标签列表获取失败!');
}
} catch (error) {
ElMessage.error('标签列表获取失败!');
} finally {
}
}
//
const reset = () =>{
state.tableData.param = {
labelName: '',
moduleName: '',
title: '',
current: 1,
size: 10,
}
getTableData();
}
//
const toAddArticle = () => {
router.push({ path: '/article/add' });
}
//
const toEditArticle = (row: any) => {
router.push({ path: '/article/edit', query: { moduleName: row.moduleName, labelName: row.labelName } });
}
//
const toShowDetail = (row: any) => {
router.push({ path: '/article/detail' , query: { moduleName: row.moduleName, labelName: row.labelName } });
}
//
const deleteArticle = (id: number) => {
ElMessageBox.confirm('确定要删除该文章吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async() => {
try {
state.tableData.loading = true;
let res = await artApi.deleteArticle(id);
if(res?.success){
await getTableData();
ElMessage.success('文章删除成功!');
} else ElMessage.error('文章删除失败!');
} catch(e) {
ElMessage.error('处理失败!');
} finally {
state.tableData.loading = false;
}
})
}
//
const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
let date = new Date(row.createtime);
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}
//
const typeFormatter = (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();
};
onMounted(() => {
getTableData();
getModuleList();
})
</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;
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-form size="large" class="login-content-form"> <el-form size="large" class="login-content-form">
<el-form-item class="login-animation1"> <el-form-item class="login-animation1">
<el-input text :placeholder="$t('message.account.accountPlaceholder1')" v-model="state.ruleForm.userName" clearable autocomplete="off"> <el-input text :placeholder="$t('message.account.accountPlaceholder1')" v-model="state.ruleForm.username" clearable autocomplete="off">
<template #prefix> <template #prefix>
<el-icon class="el-input__icon"><ele-User /></el-icon> <el-icon class="el-input__icon"><ele-User /></el-icon>
</template> </template>
@ -68,6 +68,7 @@ import { initBackEndControlRoutes } from '/@/router/backEnd';
import { Session } from '/@/utils/storage'; import { Session } from '/@/utils/storage';
import { formatAxis } from '/@/utils/formatTime'; import { formatAxis } from '/@/utils/formatTime';
import { NextLoading } from '/@/utils/loading'; import { NextLoading } from '/@/utils/loading';
import { useLoginApi } from '/@/api/login';
// //
const { t } = useI18n(); const { t } = useI18n();
@ -78,14 +79,17 @@ const router = useRouter();
const state = reactive({ const state = reactive({
isShowPassword: false, isShowPassword: false,
ruleForm: { ruleForm: {
userName: 'admin', username: 'admin1',
password: '123456', password: '123456',
code: '1234', code: '1234',
usertype: 1
}, },
loading: { loading: {
signIn: false, signIn: false,
}, },
}); });
// api
const loginApi = useLoginApi();
// //
const currentTime = computed(() => { const currentTime = computed(() => {
@ -93,21 +97,29 @@ const currentTime = computed(() => {
}); });
// //
const onSignIn = async () => { const onSignIn = async () => {
state.loading.signIn = true; try {
// token state.loading.signIn = true;
Session.set('token', Math.random().toString(36).substr(0)); let res = await loginApi.login(state.ruleForm);
// `/src/stores/userInfo.ts` if(res?.success){
Cookies.set('userName', state.ruleForm.userName); // token
if (!themeConfig.value.isRequestRoutes) { Session.set('token', 'fbc545a91cc94fe89296828a25a7e08e@9085553879028596738');
// 2 // `/src/stores/userInfo.ts`
const isNoPower = await initFrontEndControlRoutes(); Cookies.set('userName', res.data.user.realname);
signInSuccess(isNoPower); if (!themeConfig.value.isRequestRoutes) {
} else { // 2
// isRequestRoutes true const isNoPower = await initFrontEndControlRoutes();
// router No match found for location with path "/" signInSuccess(isNoPower);
const isNoPower = await initBackEndControlRoutes(); } else {
// initBackEndControlRoutes signInSuccess // isRequestRoutes true
signInSuccess(isNoPower); // router No match found for location with path "/"
const isNoPower = await initBackEndControlRoutes();
// initBackEndControlRoutes signInSuccess
signInSuccess(isNoPower);
}
}
} catch (error) {
} finally {
state.loading.signIn = false;
} }
}; };
// //

View File

@ -43,7 +43,7 @@
/* Module Resolution Options */ /* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"baseUrl": "." /* Base directory to resolve non-absolute module names. */, "baseUrl": "http://8.138.171.103:8081" /* Base directory to resolve non-absolute module names. */,
"paths": { "paths": {
"/@/*": ["src/*"] "/@/*": ["src/*"]
} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */,

View File

@ -4,6 +4,7 @@ import { defineConfig, loadEnv, ConfigEnv } from 'vite';
import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'; import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus';
import viteCompression from 'vite-plugin-compression'; import viteCompression from 'vite-plugin-compression';
import { buildConfig } from './src/utils/build'; import { buildConfig } from './src/utils/build';
import { codeInspectorPlugin } from 'code-inspector-plugin';
const pathResolve = (dir: string) => { const pathResolve = (dir: string) => {
return resolve(__dirname, '.', dir); return resolve(__dirname, '.', dir);
@ -16,8 +17,9 @@ const alias: Record<string, string> = {
const viteConfig = defineConfig((mode: ConfigEnv) => { const viteConfig = defineConfig((mode: ConfigEnv) => {
const env = loadEnv(mode.mode, process.cwd()); const env = loadEnv(mode.mode, process.cwd());
return { return {
plugins: [vue(), vueSetupExtend(), viteCompression(), JSON.parse(env.VITE_OPEN_CDN) ? buildConfig.cdn() : null], plugins: [vue(), vueSetupExtend(), viteCompression(), JSON.parse(env.VITE_OPEN_CDN) ? buildConfig.cdn() : null, codeInspectorPlugin({bundler: 'vite'})],
root: process.cwd(), root: process.cwd(),
resolve: { alias }, resolve: { alias },
base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH, base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
@ -28,6 +30,12 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
open: JSON.parse(env.VITE_OPEN), open: JSON.parse(env.VITE_OPEN),
hmr: true, hmr: true,
proxy: { proxy: {
'/vueAdminApi': {
target: 'http://8.138.171.103:8081',
ws: true,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/vueAdminApi/, ''),
},
'/gitee': { '/gitee': {
target: 'https://gitee.com', target: 'https://gitee.com',
ws: true, ws: true,