This commit is contained in:
Air 2024-12-23 18:01:57 +08:00
parent 215dde9162
commit b9a4d448e4
20 changed files with 3759 additions and 2006 deletions

View File

@ -4,7 +4,7 @@ VITE_WEB_TITLE = '固始电子报'
VITE_WEB_EN_TITLE = 'GUSHI-NEWSPAPER' VITE_WEB_EN_TITLE = 'GUSHI-NEWSPAPER'
VITE_LOGIN_TITLE = '固始电子报 管理平台' VITE_LOGIN_TITLE = '固始电子报 管理平台'
VITE_LOGIN_EN_TITLE = 'GuShi Platform' VITE_LOGIN_EN_TITLE = 'GuShi Platform'
VITE_WEB_BASE_API = '/api' VITE_WEB_BASE_API = ''
# 后端接口地址 # 后端接口地址
VITE_SERVER = 'http://csdzb.hschool.com.cn/' VITE_SERVER = 'http://csdzb.hschool.com.cn/'
# 路由模式[哈希模式 AND WEB模式 [hash | history, 这两个模式是固定死的,不能乱改值] # 路由模式[哈希模式 AND WEB模式 [hash | history, 这两个模式是固定死的,不能乱改值]

View File

@ -38,7 +38,8 @@
"sortablejs": "^1.15.3", "sortablejs": "^1.15.3",
"vue": "^3.5.12", "vue": "^3.5.12",
"vue-router": "^4.4.5", "vue-router": "^4.4.5",
"vue3-count-to": "^1.1.2" "vue3-count-to": "^1.1.2",
"vue3-puzzle-vcode": "^1.1.7"
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.25.9", "@babel/eslint-parser": "^7.25.9",

4481
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ import koi from "@/utils/axios.ts";
// 统一管理接口 // 统一管理接口
enum API { enum API {
//类型接口 //类型接口
LOGIN="/api/admin/login/login",
LIST = "/api/admin/type/index ", LIST = "/api/admin/type/index ",
ADD = "/api/admin/type/add", ADD = "/api/admin/type/add",
UPDATE = "/api/admin/type/update", UPDATE = "/api/admin/type/update",
@ -23,9 +24,18 @@ enum API {
NEWSDEL= "/api/admin/news/del", NEWSDEL= "/api/admin/news/del",
BMINFO="/api/admin/bm/find", BMINFO="/api/admin/bm/find",
NEWSADD="/api/admin/news/add", NEWSADD="/api/admin/news/add",
NEWSUPDATE="/api/admin/news/update",
BMONEADD="/api/admin/bm/addfind",
NEWSINFO="/api/admin/news/find",
//知识库
KNOWLEDGE="/api/admin/zsk/index",
KNOWLEDGEADD="/api/admin/zsk/add",
KNOWLEDGEUPDATE="/api/admin/zsk/update",
KNOWLEDGEDEL="/api/admin/zsk/del",
} }
export const Login = (data: any) => {
return koi.post(API.LOGIN, data);
};
// 根据ID进行查询 // 根据ID进行查询
export const getList = (data: any) => { export const getList = (data: any) => {
return koi.post(API.LIST, data); return koi.post(API.LIST, data);
@ -77,5 +87,25 @@ export const bmInfo = (data: any) => {
export const newsAdd = (data: any) => { export const newsAdd = (data: any) => {
return koi.post(API.NEWSADD, data); return koi.post(API.NEWSADD, data);
}; };
export const newsUpdate = (data: any) => {
return koi.post(API.NEWSUPDATE, data);
};
export const newsInfo = (data: any) => {
return koi.post(API.NEWSINFO, data);
};
export const bmOneAdd = (data: any) => {
return koi.post(API.BMONEADD, data);
};
export const knowledge = (data: any) => {
return koi.post(API.KNOWLEDGE, data);
};
export const knowledgeAdd = (data: any) => {
return koi.post(API.KNOWLEDGEADD, data);
};
export const knowledgeUpdate = (data: any) => {
return koi.post(API.KNOWLEDGEUPDATE, data);
};
export const knowledgeDel = (data: any) => {
return koi.post(API.KNOWLEDGEDEL, data);
};

View File

@ -86,9 +86,25 @@
"isFull": "1", "isFull": "1",
"isAffix": "1", "isAffix": "1",
"redirect": "" "redirect": ""
},{
"menuId": 15,
"menuName": "修改新闻",
"enName": "User Manage",
"parentId": 1,
"menuType": "2",
"path": "/paper/article/update/:id",
"name": "userPageArticleUpdate",
"component": "paper/article/update",
"icon": "Notebook",
"isHide": "0",
"isLink": "",
"isKeepAlive": "1",
"isFull": "1",
"isAffix": "1",
"redirect": ""
}, },
{ {
"menuId": 15, "menuId": 16,
"menuName": "期刊类型", "menuName": "期刊类型",
"enName": "User Manage", "enName": "User Manage",
"parentId": 1, "parentId": 1,
@ -112,7 +128,7 @@
"menuType": "1", "menuType": "1",
"path": "/knowledge", "path": "/knowledge",
"name": "Knowledge", "name": "Knowledge",
"component": "knowledge/list/index", "component": "",
"icon": "Files", "icon": "Files",
"isHide": "1", "isHide": "1",
"isLink": "", "isLink": "",
@ -120,6 +136,40 @@
"isFull": "1", "isFull": "1",
"isAffix": "1", "isAffix": "1",
"redirect": "" "redirect": ""
},
{
"menuId": 21,
"menuName": "添加关键字",
"enName": "User Manage",
"parentId":2,
"menuType": "2",
"path": "/knowledge/add",
"name": "KnowledgeAdd",
"component": "knowledge/add",
"icon": "CirclePlus",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": ""
},
{
"menuId": 22,
"menuName": "关键字列表",
"enName": "User Manage",
"parentId":2,
"menuType": "2",
"path": "/knowledge/list",
"name": "KnowledgeList",
"component": "knowledge/list",
"icon": "ScaleToOriginal",
"isHide": "1",
"isLink": "",
"isKeepAlive": "0",
"isFull": "1",
"isAffix": "1",
"redirect": ""
} }
] ]
} }

View File

@ -1,12 +1,12 @@
// 全局默认配置项 // 全局默认配置项
// 首页地址[默认] // 首页地址[默认]
export const HOME_URL: string = "/home/index"; export const HOME_URL: string = "/dist/home/index";
// 跳转子页面静态路由父级节点 // 跳转子页面静态路由父级节点
export const STATIC_URL: string = "/system/static"; export const STATIC_URL: string = "/system/static";
// 登录页地址[默认] // 登录页地址[默认]
export const LOGIN_URL: string = "/login"; export const LOGIN_URL: string = "/dist/login";
// pinia仓库前缀 // pinia仓库前缀
export const PINIA_PREFIX: string = "koi-"; export const PINIA_PREFIX: string = "koi-";

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="header-right"> <div class="header-right">
<!-- 搜索菜单 --> <!-- 搜索菜单 -->
<SearchMenu class="<md:visible"></SearchMenu> <!-- <SearchMenu class="<md:visible"></SearchMenu>-->
<!-- ElementPlus 尺寸配置 --> <!-- ElementPlus 尺寸配置 -->
<Dimension class="<md:visible"></Dimension> <Dimension class="<md:visible"></Dimension>
<!-- 路由缓存刷新 --> <!-- 路由缓存刷新 -->

View File

@ -1,18 +1,18 @@
<template> <template>
<!-- 头像 --> <!-- 头像 -->
<el-image class="w-34px h-34px rounded-full select-none user-avatar" :src="avatar"> <el-image class="w-34px h-34px rounded-full select-none user-avatar">
<template #error> <template #error>
<el-image class="w-34px h-34px rounded-full select-none user-avatar" :src="errorAvatar"></el-image> <el-avatar :size="34" icon="User" />
</template> </template>
</el-image> </el-image>
<el-dropdown class="m-l-10px" :hide-on-click="false" @command="handleCommand"> <el-dropdown class="m-l-10px" :hide-on-click="false" @command="handleCommand">
<div class="koi-dropdown"> <div class="koi-dropdown">
<div class="max-w-113px text-14px m-r-6px line-clamp-1 select-none">王将(管理员)</div> <div class="max-w-113px text-14px m-r-6px line-clamp-1 select-none">{{userInfo.userName}}</div>
<el-icon><arrow-down /></el-icon> <el-icon><arrow-down /></el-icon>
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="koiMine">{{ $t("header.personalCenter") }}</el-dropdown-item> <!-- <el-dropdown-item command="koiMine">{{ $t("header.personalCenter") }}</el-dropdown-item>-->
<el-dropdown-item command="logout">{{ $t("header.logout") }}</el-dropdown-item> <el-dropdown-item command="logout">{{ $t("header.logout") }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@ -20,13 +20,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import {onMounted, reactive, ref} from "vue";
import { koiSessionStorage, koiLocalStorage } from "@/utils/storage.ts"; import { koiSessionStorage, koiLocalStorage } from "@/utils/storage.ts";
import { LOGIN_URL } from "@/config"; import { LOGIN_URL } from "@/config";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
const router = useRouter(); const router = useRouter();
const userInfo = reactive({
userName: "",
token: "",
});
// 退 // 退
const handleLayout = () => { const handleLayout = () => {
koiSessionStorage.clear(); koiSessionStorage.clear();
@ -38,6 +41,10 @@ const handleLayout = () => {
// 退使replace // 退使replace
window.location.replace(LOGIN_URL); window.location.replace(LOGIN_URL);
}; };
onMounted(() => {
const user = koiLocalStorage.getJSON("user");
userInfo.userName = user.userName;
});
// //
const avatar = ref( const avatar = ref(
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fae90b4c7-98b6-4a47-b1b3-9ee8bc71acf6%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1692146441&t=6fca60f3a0d323869b81d8fb53b5dd1b" "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fae90b4c7-98b6-4a47-b1b3-9ee8bc71acf6%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1692146441&t=6fca60f3a0d323869b81d8fb53b5dd1b"

View File

@ -17,7 +17,8 @@ const userStore = defineStore("user", {
// 存储数据state // 存储数据state
state: (): any => { state: (): any => {
return { return {
token: "" token: "",
userName: "",
}; };
}, },
// 该函数没有上下文数据所以获取state中的变量需要使用this // 该函数没有上下文数据所以获取state中的变量需要使用this
@ -25,6 +26,9 @@ const userStore = defineStore("user", {
// Set Token // Set Token
setToken(token: string) { setToken(token: string) {
this.token = token; this.token = token;
},
setUserInfo(user: string) {
this.userName = user;
} }
}, },
// 计算属性和vuex是使用一样getters里面不是方法是计算返回的结果值 // 计算属性和vuex是使用一样getters里面不是方法是计算返回的结果值

View File

@ -43,7 +43,7 @@ class Yu {
const token = getToken(); const token = getToken();
// 如果实现挤下线功能需要用户绑定一个uuiduuid发生变化后端将数据进行处理[直接使用Sa-Token框架也阔以] // 如果实现挤下线功能需要用户绑定一个uuiduuid发生变化后端将数据进行处理[直接使用Sa-Token框架也阔以]
if (token) { if (token) {
config.headers!["Authorization"] = "Bearer " + token; config.headers!["token"] = token;
} }
return config; return config;
}, },
@ -64,17 +64,17 @@ class Yu {
// 这里的后端可能是code OR status 和 msg OR message需要看后端传递的是什么 // 这里的后端可能是code OR status 和 msg OR message需要看后端传递的是什么
// console.log("200状态", status); // console.log("200状态", status);
return res.data; return res.data;
} else if (status == 401) { } else if (status == 99) {
// console.log("401状态", status); // console.log("401状态", status);
const userStore = useUserStore(); const userStore = useUserStore();
userStore.setToken(""); // 清空token必须使用这个不能使用session清空因为登录的时候js会获取一遍token还会存在。 userStore.setToken(""); // 清空token必须使用这个不能使用session清空因为登录的时候js会获取一遍token还会存在。
koiMsgError("登录身份过期,请重新登录🌻"); koiMsgError("登录身份过期,请重新登录!");
router.replace(LOGIN_URL); router.replace(LOGIN_URL);
return Promise.reject(res.data); return Promise.reject(res.data);
} else { } else {
// console.log("后端返回数据:",res.data.msg) // console.log("后端返回数据:",res.data.msg)
koiMsgError(res.data.msg + "🌻" || "服务器偷偷跑到火星去玩了🌻"); koiMsgError(res.data.msg || "系统错误-1");
return Promise.reject(res.data.msg + "🌻" || "服务器偷偷跑到火星去玩了🌻"); // 可以将异常信息延续到页面中处理使用try{}catch(error){}; return Promise.reject(res.data.msg || "系统错误-1"); // 可以将异常信息延续到页面中处理使用try{}catch(error){};
} }
}, },
(error: any) => { (error: any) => {

View File

@ -89,7 +89,7 @@ export const koiLocalStorage = {
window.localStorage.put(PINIA_PREFIX + key, JSON.stringify(jsonValue)); window.localStorage.put(PINIA_PREFIX + key, JSON.stringify(jsonValue));
}, },
getJSON(key: string) { getJSON(key: string) {
const jsonValue: any = window.localStorage.get(PINIA_PREFIX + key); const jsonValue: any = window.localStorage.getItem(PINIA_PREFIX + key);
return JSON.parse(jsonValue); return JSON.parse(jsonValue);
} }
}; };

119
src/views/knowledge/add.vue Normal file
View File

@ -0,0 +1,119 @@
<template>
<div class="koi-flex">
<KoiCard>
<el-row :gutter="20">
<el-col :span="12">
<el-form ref="formRef" :model="form" label-width="80px" status-icon>
<el-form-item label="关键字" prop="roleName">
<el-input v-model="form.zsk_name" size="large" placeholder="请输入类型名称" clearable/>
</el-form-item>
<el-form-item label="解释" prop="roleName">
<el-card shadow="hover">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"/>
<Editor style="height: 300px; overflow-y: hidden;" v-model="form.zsk_explain" :defaultConfig="editorConfig"
@onCreated="handleCreated"/>
</el-card>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" @click="onSubmit" size="large">保存</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</KoiCard>
</div>
</template>
<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // css
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
import {
koiMsgSuccess,
koiNoticeSuccess,
koiNoticeError,
koiMsgError,
koiMsgWarning,
koiMsgBox,
koiMsgInfo
} from "@/utils/koi.ts";
// @ts-ignore
import {knowledgeAdd} from "@/api/system/post/index.ts";
import {onBeforeUnmount, ref, shallowRef, onMounted, reactive, nextTick} from 'vue'
const form = reactive({
zsk_explain: '',
zsk_name: '',
});
const onSubmit = async () => {
if (form.zsk_name == '') {
koiNoticeError("请输入关键字!");
return;
}
if (form.zsk_explain == '') {
koiNoticeError("请输入关键字解释!");
return;
}
try {
await knowledgeAdd(form);
koiNoticeSuccess("添加成功!");
} catch (error) {
console.log(error);
koiNoticeError("添加失败,请刷新重试!");
}
}
const toolbarConfig = {
showLinkImg: false,
uploadImgShowBase64: true,
excludeKeys: [
'insertVideo', //
'uploadVideo',
'group-video',
'insertImage',//
'insertLink',//
'insertTable',//
'codeBlock',//
]
}
const editorConfig = {
placeholder: '',
readOnly: false, //
autoFocus: true,
MENU_CONF: {
uploadImage: {
maxFileSize: 1 * 1024 * 1024,
server: import.meta.env.VITE_WEB_BASE_API + '/api/common/upload',
fieldName: 'file',
meta: {
association_id: 0,
},
customInsert(res, insertFn) { // TS
// customInsert(res, insertFn) { // JS
// res
console.log(res);
// res url alt href
insertFn(res.data.fullurl, '', '')
}, onError:(file, err, res)=>{
if(err.message.indexOf('exceeds maximum allowed size') !== -1){
koiNoticeError('图片限制为1M请调整好再上传');
}
},
}
}
}
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
// shallowRef
const editorRef = shallowRef();
const handleCreated = (editor) => {
editorRef.value = editor // editor
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,269 @@
<template>
<div class="koi-flex">
<KoiCard>
<!-- 表格头部按钮 -->
<el-row :gutter="10">
<el-col :span="1.5" v-auth="['system:role:add']">
<el-button type="primary" icon="plus" plain @click="handleAdd()">新增</el-button>
</el-col>
<KoiToolbar v-model:showSearch="showSearch" @refreshTable="handleListPage"></KoiToolbar>
</el-row>
<div class="h-20px"></div>
<!-- 数据表格 -->
<el-table
v-loading="loading"
border
:data="tableList"
empty-text="暂时没有数据哟"
>
<el-table-column label="ID" prop="id" width="80px" align="center" type="index"></el-table-column>
<el-table-column
label="关键字"
prop="zsk_name"
width="180px"
align="center"
:show-overflow-tooltip="true"
></el-table-column>
<el-table-column
label="解释"
prop="zsk_explain"
width="300px"
align="center"
:show-overflow-tooltip="true"
></el-table-column>
<el-table-column label="操作" align="center" width="120" fixed="right">
<template #default="{ row }">
<el-tooltip content="修改" placement="top">
<el-button
type="primary"
icon="Edit"
circle
plain
@click="handleUpdate(row)"
v-auth="['system:role:update']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
type="danger"
icon="Delete"
circle
plain
@click="handleDelete(row)"
v-auth="['system:role:delete']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div class="h-20px"></div>
<!-- {{ searchParams.pageNo }} --- {{ searchParams.pageSize }} -->
<!-- 分页 -->
<el-pagination
background
v-model:current-page="searchParams.pageNo"
v-model:page-size="searchParams.pageSize"
:page-sizes="[10, 20, 50, 100, 200]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleListPage"
@current-change="handleListPage"
/>
<!-- 添加 OR 修改 -->
<KoiDialog ref="koiDrawerRef" :width="800" :height="500" :title="title" @koiConfirm="handleConfirm"
@koiCancel="handleCancel" :loading="confirmLoading">
<template #content>
<el-form ref="formRef" :rules="rules" :model="form" label-width="80px" status-icon>
<el-row>
<el-col :sm="{ span: 20 }" :xs="{ span: 24 }">
<el-form-item label="关键字" prop="roleName">
<el-input v-model="form.zsk_name" placeholder="请输入关键字" clearable/>
</el-form-item>
<el-form-item label="解释" prop="roleName">
<el-card shadow="hover">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"/>
<Editor style="height: 250px; overflow-y: hidden;" v-model="form.zsk_explain" :defaultConfig="editorConfig"
@onCreated="handleCreated"/>
</el-card>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
</KoiDialog>
</KoiCard>
</div>
</template>
<script setup lang="ts" name="postPage">
import '@wangeditor/editor/dist/css/style.css' // css
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
import {nextTick, ref, reactive, onMounted, onBeforeUnmount, shallowRef} from "vue";
import {
koiMsgSuccess,
koiNoticeSuccess,
koiNoticeError,
koiMsgError,
koiMsgWarning,
koiMsgBox,
koiMsgInfo
} from "@/utils/koi.ts";
// @ts-ignore
import {getList, add, update, del, knowledge, knowledgeUpdate, knowledgeDel} from "@/api/system/post/index.ts";
import {useRouter} from "vue-router";
const router = useRouter();
// Loading
const loading = ref(false);
// []
const showSearch = ref<boolean>(true); //
//
const tableList = ref<any>();
const total = ref(0);
//
const searchParams = reactive({
pageNo: 1, //
pageSize: 10, //
});
/** @current-change点击分页组件页码发生变化例如切换第2、3页 OR 上一页 AND 下一页 OR 跳转某一页 */
/** @size-change点击分页组件下拉选中条数发生变化例如选择10条/页、20条/页等 */
// @current-change AND @size-change
/** 数据表格 */
const handleListPage = async () => {
try {
loading.value = true;
tableList.value = []; //
const res: any = await knowledge(searchParams);
tableList.value = res.data;
total.value = res.data.length;
loading.value = false;
} catch (error) {
console.log(error);
koiNoticeError("数据查询失败,请刷新重试!");
}
};
onMounted(() => {
//
handleListPage();
});
/** 添加 */
const handleAdd = () => {
//
router.push("/knowledge/add");
};
const postId = ref(0);
/** 修改 */
const handleUpdate = (row) => {
console.log(row);
//
koiDrawerRef.value.koiOpen();
//
//
title.value = "关键字修改";
postId.value = row.id;
form.zsk_name = row.zsk_name;
form.zsk_explain = row.zsk_explain;
//console.log(postId);
//
//handleEcho(postId);
};
// OR Ref
const koiDrawerRef = ref();
//
const title = ref("岗位类型管理");
// formRef
const formRef = ref<any>();
// form
const form = reactive({
zsk_name: "",
zsk_explain: ""
});
/** 表单规则 */
const rules = reactive({
postName: [{required: true, message: "请输入岗位名字", trigger: "blur"}],
postCode: [{required: true, message: "请输入岗位编码", trigger: "blur"}],
postStatus: [{required: true, message: "请输入选择岗位状态", trigger: "blur"}]
});
// Loading
const confirmLoading = ref(false);
/** 确定 */
const handleConfirm = () => {
if (!formRef.value) return;
confirmLoading.value = true;
(formRef.value as any).validate(async (valid: any) => {
if (valid) {
console.log("表单ID", postId.value);
try {
form.id = postId.value;
await knowledgeUpdate(form);
koiNoticeSuccess("修改成功!");
confirmLoading.value = false;
koiDrawerRef.value.koiQuickClose();
handleListPage();
} catch (error) {
console.log(error);
confirmLoading.value = false;
koiNoticeError("修改失败,请刷新重试!");
}
} else {
koiMsgError("验证失败,请检查填写内容🌻");
confirmLoading.value = false;
}
});
};
/** 取消 */
const handleCancel = () => {
koiDrawerRef.value.koiClose();
};
/** 删除 */
const handleDelete = (row: any) => {
const id = row.id;
if (id == null || id == "") {
koiMsgWarning("请选中需要删除的数据!");
return;
}
koiMsgBox("您确认需要删除[" + row.zsk_name + "]么?")
.then(async () => {
try {
await knowledgeDel({id: id});
handleListPage();
koiNoticeSuccess("删除成功🌻");
} catch (error) {
console.log(error);
handleListPage();
koiNoticeError("删除失败,请刷新重试🌻");
}
})
.catch(() => {
koiMsgError("已取消🌻");
});
};
const toolbarConfig = {}
const editorConfig = {placeholder: '请输入内容...'}
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
// shallowRef
const editorRef = shallowRef();
const handleCreated = (editor) => {
editorRef.value = editor // editor
}
</script>
<style lang="scss" scoped></style>

View File

@ -16,7 +16,7 @@
<!-- 备案号--> <!-- 备案号-->
<div class="beianhao select-none <md:hidden"> <div class="beianhao select-none <md:hidden">
<a class="chroma-text" href="https://beian.miit.gov.cn/" target="_blank" <a class="chroma-text" href="https://beian.miit.gov.cn/" target="_blank"
>{{ $t("login.beianhao") }}豫ICP备123456789号-123</a >{{ $t("login.beianhao") }}豫ICP备123456789号-PC</a
> >
</div> </div>
</el-col> </el-col>
@ -44,21 +44,22 @@
v-model="loginForm.password" v-model="loginForm.password"
/> />
</el-form-item> </el-form-item>
<el-form-item prop="securityCode"> <!-- <el-form-item prop="securityCode">-->
<el-input <!-- <el-input-->
type="text" <!-- type="text"-->
:placeholder="$t('login.security')" <!-- :placeholder="$t('login.security')"-->
:suffix-icon="Open" <!-- :suffix-icon="Open"-->
v-model="loginForm.securityCode" <!-- v-model="loginForm.securityCode"-->
@keydown.enter="handleKoiLogin" <!-- @keydown.enter="handleKoiLogin"-->
></el-input> <!-- ></el-input>-->
</el-form-item> <!-- </el-form-item>-->
<el-form-item> <el-form-item>
<el-image class="w-100px h-30px" :src="loginForm.captchaPicture" @click="handleCaptcha" /> <!-- <el-image class="w-100px h-30px" :src="loginForm.captchaPicture" @click="handleCaptcha" />-->
<el-button text size="small" class="ml-6px" @click="handleCaptcha"> <!-- <el-button text size="small" class="ml-6px" @click="handleCaptcha">-->
<div class="text-gray-400 hover:text-#8B5CF6 select-none">{{ $t("login.blur") }}</div> <!-- <div class="text-gray-400 hover:text-#8B5CF6 select-none">{{ $t("login.blur") }}</div>-->
</el-button> <!-- </el-button>-->
</el-form-item> </el-form-item>
<!-- 登录按钮 --> <!-- 登录按钮 -->
<el-form-item> <el-form-item>
<el-button <el-button
@ -66,7 +67,7 @@
v-if="!loading" v-if="!loading"
class="w-245px bg-[--el-color-primary]" class="w-245px bg-[--el-color-primary]"
round round
v-throttle:3000="handleKoiLogin" v-throttle:3000="onShowLogin"
>{{ $t("login.in") }}</el-button >{{ $t("login.in") }}</el-button
> >
<el-button type="primary" v-else class="w-245px bg-[--el-color-primary]" round :loading="loading">{{ <el-button type="primary" v-else class="w-245px bg-[--el-color-primary]" round :loading="loading">{{
@ -82,12 +83,13 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<Vcode :show="isShow" @success="onSuccess"/>
<!-- <KoiLoading></KoiLoading>--> <!-- <KoiLoading></KoiLoading>-->
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Vcode from 'vue3-puzzle-vcode'
import { User, Lock, Open } from "@element-plus/icons-vue"; import { User, Lock, Open } from "@element-plus/icons-vue";
// @ts-ignore // @ts-ignore
import { ref, reactive, onMounted, onUnmounted, computed } from "vue"; import { ref, reactive, onMounted, onUnmounted, computed } from "vue";
@ -95,7 +97,7 @@ import { ref, reactive, onMounted, onUnmounted, computed } from "vue";
import type { FormInstance, FormRules } from "element-plus"; import type { FormInstance, FormRules } from "element-plus";
import { koiMsgWarning, koiMsgError } from "@/utils/koi.ts"; import { koiMsgWarning, koiMsgError } from "@/utils/koi.ts";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
// import { koiLogin, getCaptcha } from "@/api/system/login/index.ts"; import { Login } from "@/api/system/post/index.ts";
import authLogin from "@/assets/json/authLogin.json"; import authLogin from "@/assets/json/authLogin.json";
import useUserStore from "@/stores/modules/user.ts"; import useUserStore from "@/stores/modules/user.ts";
import useKeepAliveStore from "@/stores/modules/keepAlive.ts"; import useKeepAliveStore from "@/stores/modules/keepAlive.ts";
@ -109,7 +111,7 @@ import KoiDark from "./components/KoiDark.vue";
import KoiLanguage from "./components/KoiLanguage.vue"; import KoiLanguage from "./components/KoiLanguage.vue";
import { getLanguage } from "@/utils/index.ts"; import { getLanguage } from "@/utils/index.ts";
import useGlobalStore from "@/stores/modules/global.ts"; import useGlobalStore from "@/stores/modules/global.ts";
const isShow = ref(false);
// //
const loginTitle = ref(settings.loginTitle); const loginTitle = ref(settings.loginTitle);
loginTitle.value = computed(() => { loginTitle.value = computed(() => {
@ -137,11 +139,8 @@ interface ILoginUser {
} }
const loginForm = reactive<ILoginUser>({ const loginForm = reactive<ILoginUser>({
loginName: "yuadmin", loginName: "admin",
password: "123456", password: "admin888",
securityCode: "1234",
codeKey: "",
captchaPicture: ""
}); });
let loginRules: any = reactive<FormRules<ILoginUser>>({}); let loginRules: any = reactive<FormRules<ILoginUser>>({});
@ -161,60 +160,33 @@ loginRules = computed(() => {
} }
}); });
/** 获取验证码 */
// const handleCaptcha = async () => {
// try {
// const res: any = await getCaptcha();
// loginForm.codeKey = res.data.codeKey;
// loginForm.captchaPicture = res.data.captchaPicture;
// } catch (error) {
// console.log(error);
// koiMsgError("🌻");
// }
// };
// const koiTimer = ref();
// //
// const getCaptchaTimer = () => {
// koiTimer.value = setInterval(() => {
// //
// handleCaptcha();
// }, 345 * 1000);
// };
// //
onMounted(() => { onMounted(() => {
//
//handleCaptcha();
//
// getCaptchaTimer();
}); });
const onSuccess = () => {
// onUnmounted(() => { isShow.value = false;
// // handleKoiLogin();
// clearInterval(koiTimer.value); };
// koiTimer.value = null; const onShowLogin = () => {
// }); isShow.value = true;
}
/** 登录 */ /** 登录 */
const handleKoiLogin = () => { const handleKoiLogin = () => {
if (!loginFormRef.value) return; if (!loginFormRef.value) return;
(loginFormRef.value as any).validate(async (valid: any, fields: any) => { (loginFormRef.value as any).validate(async (valid: any, fields: any) => {
// @ts-ignore // @ts-ignore
const loginName = loginForm.loginName; const username = loginForm.loginName;
// @ts-ignore // @ts-ignore
const password = loginForm.password; const password = loginForm.password;
// @ts-ignore
const securityCode = loginForm.securityCode;
// @ts-ignore
const codeKey = loginForm.codeKey;
if (valid) { if (valid) {
loading.value = true; loading.value = true;
try { try {
// 1 // 1
// const res: any = await koiLogin({ loginName, password, codeKey, securityCode }); const res: any = await Login({ username, password});
// userStore.setToken(res.data.tokenValue); userStore.setToken(res.data.token);
userStore.setToken(authLogin.data.tokenValue); userStore.setUserInfo(res.data.user.username);
//userStore.setToken(authLogin.data.tokenValue);
// 2 AND AND AND // 2 AND AND AND
if (userStore?.token) { if (userStore?.token) {
await initDynamicRouter(); await initDynamicRouter();

View File

@ -32,12 +32,12 @@
<div>{{ item.bm_name ? item.bm_name : '版面' }}</div> <div>{{ item.bm_name ? item.bm_name : '版面' }}</div>
<div> <div>
<el-space wrap :size="30"> <el-space wrap :size="30">
<el-icon class="cursor-pointer" @click="left_right('l',index)"> <!-- <el-icon class="cursor-pointer" @click="left_right('l',index)">-->
<ArrowLeftBold/> <!-- <ArrowLeftBold/>-->
</el-icon> <!-- </el-icon>-->
<el-icon class="cursor-pointer" @click="left_right('r',index)"> <!-- <el-icon class="cursor-pointer" @click="left_right('r',index)">-->
<ArrowRightBold/> <!-- <ArrowRightBold/>-->
</el-icon> <!-- </el-icon>-->
<el-icon class="cursor-pointer" @click="del(index)"> <el-icon class="cursor-pointer" @click="del(index)">
<DeleteFilled/> <DeleteFilled/>
</el-icon> </el-icon>
@ -49,13 +49,18 @@
<el-form-item label="版面名称"> <el-form-item label="版面名称">
<el-input v-model="item.bm_name" placeholder="输入版面名称"/> <el-input v-model="item.bm_name" placeholder="输入版面名称"/>
</el-form-item> </el-form-item>
<el-form-item label="版面排序">
<el-input @blur="addSort" type="number" v-model="item.weight" placeholder="输入版面排序"/>
</el-form-item>
<el-form-item label="版面PDF"> <el-form-item label="版面PDF">
<KoiUploadFiles :fileList="item.pdf" acceptType=".pdf" @update:fileList="(file) => updateFileList(file, index)" @fileSuccess="(file) => getFileList(file, index)"> <KoiUploadFiles :fileList="item.pdf" acceptType=".pdf" @update:fileList="(file) => updateFileList(file, index)"
@fileSuccess="(file) => getFileList(file, index)">
<template #tip>PDF最大为 10M</template> <template #tip>PDF最大为 10M</template>
</KoiUploadFiles> </KoiUploadFiles>
</el-form-item> </el-form-item>
<el-form-item label="版面图片" prop="avatar"> <el-form-item label="版面图片" prop="avatar">
<KoiUploadImage :imageUrl="item.bm_img" @update:imageUrl="(file) => getImgList(file, index)" width="150px" height="150px"> <KoiUploadImage :imageUrl="item.bm_img" @update:imageUrl="(file) => getImgList(file, index)" width="150px"
height="150px">
<template #content> <template #content>
<el-icon> <el-icon>
<Picture/> <Picture/>
@ -88,7 +93,8 @@ import {
koiMsgInfo koiMsgInfo
} from "@/utils/koi.ts"; } from "@/utils/koi.ts";
import useTabsStore from "@/stores/modules/tabs.ts"; import useTabsStore from "@/stores/modules/tabs.ts";
import { useRoute, useRouter } from "vue-router"; import {useRoute, useRouter} from "vue-router";
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const tabsStore = useTabsStore(); const tabsStore = useTabsStore();
@ -100,7 +106,7 @@ const getFileList = (d, index) => {
const updateFileList = (d, index) => { const updateFileList = (d, index) => {
console.log(d); console.log(d);
console.log(index); console.log(index);
backArr.value[index].bm_pdf = d.fullurl?d.fullurl:''; backArr.value[index].bm_pdf = d.fullurl ? d.fullurl : '';
} }
const getImgList = (d, index) => { const getImgList = (d, index) => {
console.log(d); console.log(d);
@ -109,14 +115,22 @@ const getImgList = (d, index) => {
} }
const backArr = ref([ const backArr = ref([
{bm_name: '', bm_img: '', bm_pdf:'',pdf: []} {bm_name: '', bm_img: '', bm_pdf: '', weight: 0, pdf: []}
]); ]);
const form = reactive({ const form = reactive({
datetime: "", datetime: "",
type_id: "" type_id: ""
}); });
const addBlack = () => { const addBlack = () => {
backArr.value.push({bm_name: '', bm_img: '',bm_pdf:'', pdf: []}); // backArr weight
const maxWeight = Math.max(...backArr.value.map(item => item.weight), 0);
// weight maxWeight + 1
backArr.value.push({bm_name: '', bm_img: '', bm_pdf: '', weight: maxWeight + 1, pdf: []});
}
const addSort = () => {
//weight
backArr.value.sort((a, b) => a.weight - b.weight);
} }
onMounted(() => { onMounted(() => {
getTypelist(); getTypelist();
@ -146,7 +160,7 @@ const handleMineSave = async () => {
return; return;
} }
} }
const data = { date: form, bm: backArr.value }; const data = {date: form, bm: backArr.value};
try { try {
await bmAdd(data); await bmAdd(data);
koiNoticeSuccess("保存成功!"); koiNoticeSuccess("保存成功!");

View File

@ -68,6 +68,7 @@ const form = reactive({
subtitle: '', subtitle: '',
reporter: '', reporter: '',
content: '', content: '',
coordinate_show:'',
}); });
/*新闻坐标*/ /*新闻坐标*/
const coordinate = ref(); const coordinate = ref();
@ -93,8 +94,27 @@ const getBmInfo = async () => {
/*文章保存*/ /*文章保存*/
const onSubmit = async () => { const onSubmit = async () => {
form.coordinate = coordinate.value; form.coordinate = coordinate.value;
form.bm_id = id.value; form.bm_id = id.value;
console.log(form); console.log(form);
if(form.new_name==''){
koiNoticeError("新闻标题不能为空!");
return;
}
if(form.reporter==''){
koiNoticeError("新闻记者不能为空!");
return;
}
if(form.coordinate=='' || typeof (form.coordinate)=='undefined'){
koiNoticeError("新闻坐标不能为空!");
return;
}
if(form.content==''){
koiNoticeError("新闻内容不能为空!");
return;
}
form.coordinate_show=`${rectCoordinates.value.x1},${rectCoordinates.value.y1},${rectCoordinates.value.x2},${rectCoordinates.value.y2}`;
try { try {
const res: any = await newsAdd(form); const res: any = await newsAdd(form);
console.log("菜单数据表格数据->", res.data); console.log("菜单数据表格数据->", res.data);
@ -107,8 +127,45 @@ const onSubmit = async () => {
koiNoticeError("数据添加失败,请刷新重试🌻"); koiNoticeError("数据添加失败,请刷新重试🌻");
} }
} }
const toolbarConfig = {} const toolbarConfig = {
const editorConfig = {placeholder: '请输入内容...'} showLinkImg: false,
uploadImgShowBase64: true,
excludeKeys: [
'insertVideo', //
'uploadVideo',
'group-video',
'insertImage',//
'insertLink',//
'insertTable',//
'codeBlock',//
]
}
const editorConfig = {
placeholder: '',
readOnly: false, //
autoFocus: true,
MENU_CONF: {
uploadImage: {
maxFileSize: 1 * 1024 * 1024,
server: import.meta.env.VITE_WEB_BASE_API + '/api/common/upload',
fieldName: 'file',
meta: {
association_id: 0,
},
customInsert(res, insertFn) { // TS
// customInsert(res, insertFn) { // JS
// res
console.log(res);
// res url alt href
insertFn(res.data.fullurl, '', '')
}, onError:(file, err, res)=>{
if(err.message.indexOf('exceeds maximum allowed size') !== -1){
koiNoticeError('图片限制为1M请调整好再上传');
}
},
}
}
}
// //
onBeforeUnmount(() => { onBeforeUnmount(() => {
const editor = editorRef.value const editor = editorRef.value

View File

@ -0,0 +1,431 @@
<template>
<div class="koi-flex" v-if="bmInfo">
<KoiCard>
<el-row :gutter="20">
<el-col :span="8">
<div style="padding: 0px 20px 20px 0px;font-weight: 600">请在图片边框内选定区域</div>
<el-card :body-style="{padding:'0'}" style="padding: 0;position:relative;">
<div @mousedown="startDrag" @mousemove="onDrag" @mouseup="endDrag" @mouseleave="endDrag" style="width: 100%">
<!-- 图片 -->
<img draggable="false" id="image-selector" :src="bmInfoData.bm_img" ref="image" alt="image"
@click="handleClick"
style="cursor: crosshair; position: relative; user-select: none; width: 100%; height: auto;"
@load="onImageLoad"/>
<!-- 画布用于绘制矩形路径 -->
<canvas id="myCanvas" ref="canvas" :width="imageWidth" :height="imageHeight"
style="position: absolute; top: 0; left: 0; pointer-events: none;">
</canvas>
</div>
</el-card>
</el-col>
<el-col :span="16">
<el-form :model="form" label-width="auto">
<el-form-item label="新闻标题">
<el-input v-model="form.new_name" size="large"/>
</el-form-item>
<el-form-item label="新闻副标题">
<el-input v-model="form.subtitle" size="large"/>
</el-form-item>
<el-form-item label="新闻记者">
<el-input v-model="form.reporter" size="large"/>
</el-form-item>
<el-form-item label="新闻坐标">
<el-input disabled v-model="coordinate" size="large"/>
<el-button class="mt-2" @click="allowClickTrue()">重新选择</el-button>
</el-form-item>
<el-form-item label="新闻详情">
<el-card shadow="hover">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"/>
<Editor style="height: 300px; overflow-y: hidden;" v-model="form.content" :defaultConfig="editorConfig"
@onCreated="handleCreated"/>
</el-card>
</el-form-item>
<el-form-item label=" ">
<el-button type="primary" @click="onSubmit" size="large">保存新闻</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</KoiCard>
</div>
</template>
<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // css
import {getAssets} from "@/utils/index.ts";
import {onBeforeUnmount, ref, shallowRef, onMounted, reactive, nextTick} from 'vue'
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
import {bmInfo, newsAdd, newsInfo, newsUpdate} from "@/api/system/post";
import {koiNoticeError, koiNoticeSuccess} from "@/utils/koi.ts";
import {useRoute, useRouter} from 'vue-router';
import useTabsStore from "@/stores/modules/tabs.ts"; // useRoute
const router = useRouter();
const tabsStore = useTabsStore();
const form = reactive({
new_name: '',
coordinate: '',
coordinate_show:'',
subtitle: '',
reporter: '',
content: '',
});
/*新闻坐标 真实的*/
const coordinate = ref();
/*新闻坐标 展示的*/
const coordinateShow = ref();
/*版面详情*/
const bmInfoData = ref({});
const route = useRoute(); // route
const id = ref();
onMounted(() => {
id.value = route.params.id;
getNewsInfo();
})
const getNewsInfo = async () => {
try {
const res: any = await newsInfo({id: id.value});
console.log("菜单数据表格数据->", res.data);
form.new_name = res.data.new_name;
form.subtitle = res.data.subtitle;
form.reporter = res.data.reporter;
form.content = res.data.content;
form.id = res.data.id;
form.coordinate = res.data.coordinate;
form.coordinate_show=res.data.coordinate_show;
coordinate.value = res.data.coordinate;
coordinateShow.value = res.data.coordinate_show;
await getBmInfo(res.data.bm_id);
//bmInfoData.value = res.data;
// koiNoticeSuccess("!");
} catch (error) {
console.log(error);
koiNoticeError("数据查询失败,请刷新重试🌻");
}
}
const getBmInfo = async (bm_id) => {
try {
const res: any = await bmInfo({id: bm_id});
console.log("菜单数据表格数据->", res.data);
bmInfoData.value = res.data;
var coordArray = coordinateShow.value.split(",");
var sArray = coordinate.value.split(",");
var xx1 = parseInt(sArray[0]);
var yy1 = parseInt(sArray[1]);
var xx2 = parseInt(sArray[2]);
var yy2 = parseInt(sArray[3]);
s.value = {xx1, yy1, xx2, yy2};
var x1 = parseInt(coordArray[0]);
var y1 = parseInt(coordArray[1]);
var x2 = parseInt(coordArray[2]);
var y2 = parseInt(coordArray[3]);
updateDrawRectanglePath(x1, y1, x2, y2);
// koiNoticeSuccess("!");
} catch (error) {
console.log(error);
koiNoticeError("数据查询失败,请刷新重试");
}
}
const updateDrawRectanglePath = (x1, y1, x2, y2) => {
rectCoordinates.value = {x1, y1, x2, y2};
//
rect.value = {
left: `${x1}px`,
top: `${y1}px`,
width: `${x2 - x1}px`,
height: `${y2 - y1}px`,
};
// 使 nextTick DOM 访 canvas
nextTick(() => {
setTimeout(() => {
drawRectanglePath(x1, y1, x2, y2);
}, 500);
});
//
points.value = [];
}
/*文章保存*/
const onSubmit = async () => {
form.coordinate = coordinate.value;
form.coordinate_show=coordinateShow.value ;
form.bm_id = id.value;
console.log(form);
try {
const res: any = await newsUpdate(form);
console.log("菜单数据表格数据->", res.data);
bmInfoData.value = res.data;
koiNoticeSuccess("修改成功!");
tabsStore.removeTab(route.fullPath);
router.push('/paper/list');
} catch (error) {
console.log(error);
koiNoticeError("数据添加失败,请刷新重试🌻");
}
}
const toolbarConfig = {
showLinkImg: false,
uploadImgShowBase64: true,
excludeKeys: [
'insertVideo', //
'uploadVideo',
'group-video',
'insertImage',//
'insertLink',//
'insertTable',//
'codeBlock',//
]
}
const editorConfig = {
placeholder: '',
readOnly: false, //
autoFocus: true,
MENU_CONF: {
uploadImage: {
maxFileSize: 1 * 1024 * 1024,
server: import.meta.env.VITE_WEB_BASE_API + '/api/common/upload',
fieldName: 'file',
meta: {
association_id: 0,
},
customInsert(res, insertFn) { // TS
// customInsert(res, insertFn) { // JS
// res
console.log(res);
// res url alt href
insertFn(res.data.fullurl, '', '')
}, onError:(file, err, res)=>{
if(err.message.indexOf('exceeds maximum allowed size') !== -1){
koiNoticeError('图片限制为1M请调整好再上传');
}
},
}
}
}
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor // editor
}
//
const points = ref([]); //
/*新闻坐标*/
const s = ref();
// shallowRef
const editorRef = shallowRef();
const showPoints = ref([]); //
const rectCoordinates = ref(null); //
const rect = ref(null); // overlay
const imageWidth = ref(0); //
const imageHeight = ref(0); //
const imageLoaded = ref(false); //
const isDragging = ref(false); //
const dragStart = ref({x: 0, y: 0}); //
const allowClick = ref(true); //
const allowClickTrue = () => {
console.log("allowClickTrue");
//
points.value = [];
allowClick.value = true;
isDragging.value = false;
rectCoordinates.value = null;
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext("2d");
//
context.clearRect(0, 0, canvas.width, canvas.height);
}
//
const onImageLoad = () => {
const img = document.getElementById('image-selector');
imageWidth.value = img.offsetWidth; // 使
imageHeight.value = img.offsetHeight; // 使
imageLoaded.value = true;
console.log('Image loaded:', imageWidth.value, imageHeight.value);
};
//
const handleClick = (event) => {
if (!imageLoaded.value || !allowClick.value) return;
const img = document.getElementById('image-selector');
const rect = img.getBoundingClientRect(); // 使 img
const scaleX = img.naturalWidth / img.offsetWidth; //
const scaleY = img.naturalHeight / img.offsetHeight; //
const xx = parseInt((event.clientX - rect.left) * scaleX); // 使
const yy = parseInt((event.clientY - rect.top) * scaleY); // 使
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
//
points.value.push({x, y});
showPoints.value.push({xx, yy});
console.log(`Clicked at: (${x}, ${y})`); //
console.log(`Clicked at a: (${xx}, ${yy})`); //
// canvas
const canvas = document.getElementById('myCanvas');
console.log('Canvas:', canvas);
if (canvas) {
drawCurrentPath(); //
} else {
console.error("Canvas element not found!");
}
//
if (points.value.length === 4) {
calculateRectangle();
showCalculateRectangle();
}
};
// 线
const drawCurrentPath = () => {
const canvas = document.getElementById('myCanvas');
if (!canvas) {
console.error("Canvas element not found!");
return;
}
const context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height); //
//
points.value.forEach(point => {
context.beginPath();
context.arc(point.x, point.y, 5, 0, Math.PI * 2); //
context.fillStyle = "red"; //
context.fill();
});
// 线
if (points.value.length > 1) {
context.beginPath();
context.moveTo(points.value[0].x, points.value[0].y);
for (let i = 1; i < points.value.length; i++) {
context.lineTo(points.value[i].x, points.value[i].y);
}
context.strokeStyle = "blue"; // 线
context.lineWidth = 2;
context.stroke();
}
};
//
const showCalculateRectangle = () => {
const [p1, p2, p3, p4] = showPoints.value;
console.log('showCalculateRectangle', showPoints.value);
const xx1 = Math.min(p1.xx, p2.xx, p3.xx, p4.xx);
const yy1 = Math.min(p1.yy, p2.yy, p3.yy, p4.yy);
const xx2 = Math.max(p1.xx, p2.xx, p3.xx, p4.xx);
const yy2 = Math.max(p1.yy, p2.yy, p3.yy, p4.yy);
s.value = {xx1, yy1, xx2, yy2};
console.log('s', s.value);
coordinate.value = `${xx1},${yy1},${xx2},${yy2}`;
};
//
const calculateRectangle = () => {
const [p1, p2, p3, p4] = points.value;
console.log('calculateRectangle', points.value);
const x1 = Math.min(p1.x, p2.x, p3.x, p4.x);
const y1 = Math.min(p1.y, p2.y, p3.y, p4.y);
const x2 = Math.max(p1.x, p2.x, p3.x, p4.x);
const y2 = Math.max(p1.y, p2.y, p3.y, p4.y);
rectCoordinates.value = {x1, y1, x2, y2};
//
rect.value = {
left: `${x1}px`,
top: `${y1}px`,
width: `${x2 - x1}px`,
height: `${y2 - y1}px`,
};
// 使 nextTick DOM 访 canvas
nextTick(() => {
drawRectanglePath(x1, y1, x2, y2);
});
//
points.value = [];
};
// 使 Canvas
const drawRectanglePath = (x1, y1, x2, y2) => {
console.log('Drawing rectangle path...');
console.log('x1', x1)
console.log('y1', y1)
console.log('x2', x2)
console.log('y3', y2)
coordinateShow.value = `${x1},${y1},${x2},${y2}`;
const canvas = document.getElementById('myCanvas');
if (!canvas) {
console.error("Canvas element not found!");
return;
}
const context = canvas.getContext("2d");
//
context.clearRect(0, 0, canvas.width, canvas.height);
//
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y1);
context.lineTo(x2, y2);
context.lineTo(x1, y2);
context.closePath();
//
context.lineWidth = 3;
context.strokeStyle = "green"; // 绿
context.setLineDash([5, 3]); // 线
context.stroke();
};
//
const startDrag = (event: MouseEvent) => {
console.log(rectCoordinates.value);
//event.preventDefault(); //
if (rectCoordinates.value) { //
isDragging.value = true;
allowClick.value = false; //
dragStart.value = {x: event.clientX, y: event.clientY};
}
};
//
const onDrag = (event) => {
if (isDragging.value) {
const dx = event.clientX - dragStart.value.x;
const dy = event.clientY - dragStart.value.y;
//
rectCoordinates.value.x1 += dx;
rectCoordinates.value.y1 += dy;
rectCoordinates.value.x2 += dx;
rectCoordinates.value.y2 += dy;
console.log( s.value)
//
s.value.xx1 += dx;
s.value.yy1 += dy;
s.value.xx2 += dx;
s.value.yy2 += dy;
coordinate.value = `${s.value.xx1},${s.value.yy1},${s.value.xx2},${s.value.yy2}`;
//
dragStart.value = {x: event.clientX, y: event.clientY};
//
drawRectanglePath(rectCoordinates.value.x1, rectCoordinates.value.y1, rectCoordinates.value.x2, rectCoordinates.value.y2);
}
};
//
const endDrag = () => {
isDragging.value = false;
};
</script>
<style scoped lang="scss">
/* 你的样式 */
</style>

View File

@ -48,7 +48,7 @@
:indent="30" :indent="30"
:data="tableList" :data="tableList"
:default-expand-all="isExpandAll" :default-expand-all="isExpandAll"
row-key="id" row-key="uuid"
@row-click="rowClick" @row-click="rowClick"
:lazy="true" :lazy="true"
:load="load" :load="load"
@ -60,9 +60,12 @@
prop="datetime" prop="datetime"
align="left" align="left"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
width="250px" width="300px"
> >
<template #default="scope"> <template #default="scope">
<el-input @blur="addSort(scope.row)" v-if="scope.row.level==2" :maxlength="2" class="center-input"
style="max-width: 50px;margin-right: 10px"
v-model="scope.row.weight"></el-input>
<span class="cursor-pointer">{{ scope.row.datetime }}</span> <span class="cursor-pointer">{{ scope.row.datetime }}</span>
</template> </template>
</el-table-column> </el-table-column>
@ -117,7 +120,7 @@
icon="Edit" icon="Edit"
circle circle
plain plain
@click="handleUpdate(row)" @click="openUrl('/paper/article/update/'+row.id)"
></el-button> ></el-button>
<el-button v-if="row.level==3" <el-button v-if="row.level==3"
type="danger" type="danger"
@ -168,7 +171,8 @@
</el-row> </el-row>
</template> </template>
</KoiDialog> </KoiDialog>
<KoiDialog ref="koiDrawerBm" :width="500" :height="400" title="版面编辑" @koiConfirm="handleConfirmBmDo" <KoiDialog ref="koiDrawerBm" :width="500" :height="400" :title="Bmform.id==0?'添加版面':'修改版面'"
@koiConfirm="handleConfirmBmDo"
@koiCancel="handleCancel" :loading="confirmLoading"> @koiCancel="handleCancel" :loading="confirmLoading">
<template #content> <template #content>
<el-row> <el-row>
@ -177,6 +181,9 @@
<el-form-item label="版面名称"> <el-form-item label="版面名称">
<el-input v-model="Bmform.bm_name" placeholder="输入版面名称"/> <el-input v-model="Bmform.bm_name" placeholder="输入版面名称"/>
</el-form-item> </el-form-item>
<el-form-item label="版面排序">
<el-input type="number" v-model="Bmform.weight" placeholder="输入版面排序"/>
</el-form-item>
<el-form-item label="版面PDF"> <el-form-item label="版面PDF">
<KoiUploadFiles :fileList="Bmform.pdf" acceptType=".pdf" @update:fileList="updateFileList" <KoiUploadFiles :fileList="Bmform.pdf" acceptType=".pdf" @update:fileList="updateFileList"
@fileSuccess="getFileList"> @fileSuccess="getFileList">
@ -194,6 +201,7 @@
<template #tip>图片最大为 3M</template> <template #tip>图片最大为 3M</template>
</KoiUploadImage> </KoiUploadImage>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-col> </el-col>
</el-row> </el-row>
@ -206,9 +214,22 @@
<script setup lang="ts" name="menuPage"> <script setup lang="ts" name="menuPage">
import {nextTick, ref, reactive, onMounted} from "vue"; import {nextTick, ref, reactive, onMounted} from "vue";
import {koiNoticeSuccess, koiNoticeError, koiMsgError, koiMsgWarning, koiMsgBox, koiMsgInfo} from "@/utils/koi.ts"; import {koiNoticeSuccess, koiNoticeError, koiMsgError, koiMsgWarning, koiMsgBox, koiMsgInfo} from "@/utils/koi.ts";
import {generateUUID} from "@/utils/index.ts";
import {useRoute, useRouter} from "vue-router"; import {useRoute, useRouter} from "vue-router";
import {bmDel, bmList, bmListNews, bmListNext, bmUpdate, dateDel, dateUpdate, getList, newsDel} from "@/api/system/post/index.ts"; import {
bmDel,
bmList,
bmListNews,
bmListNext,
bmUpdate,
dateDel,
dateUpdate,
getList,
newsDel,
bmOneAdd
} from "@/api/system/post/index.ts";
import useTabsStore from "@/stores/modules/tabs.ts";
const tabsStore = useTabsStore();
const router = useRouter(); const router = useRouter();
// Loading // Loading
const loading = ref(false); const loading = ref(false);
@ -256,10 +277,12 @@ const updateFileList = (d) => {
/*PDF上传*/ /*PDF上传*/
const getFileList = (d) => { const getFileList = (d) => {
console.log(d); console.log(d);
Bmform.bm_pdf = d.fullurl;
} }
/*IMG上传*/ /*IMG上传*/
const getImgList = (d) => { const getImgList = (d) => {
console.log(d); console.log(d);
Bmform.bm_img = d;
} }
/*获取PDF文件名称*/ /*获取PDF文件名称*/
const getFileNameFromUrl = (url) => { const getFileNameFromUrl = (url) => {
@ -286,6 +309,7 @@ const load = async (row, treeNode, resolve) => {
res.data = res.data.map(item => { res.data = res.data.map(item => {
return { return {
...item, ...item,
uuid:generateUUID(),
datetime: item.bm_name, datetime: item.bm_name,
bm_name: undefined // bm_name: undefined //
}; };
@ -295,6 +319,7 @@ const load = async (row, treeNode, resolve) => {
res.data = res.data.map(item => { res.data = res.data.map(item => {
return { return {
...item, ...item,
uuid:generateUUID(),
datetime: item.new_name, datetime: item.new_name,
bm_name: undefined // bm_name: undefined //
}; };
@ -312,7 +337,9 @@ const Bmform = reactive({
bm_img: "", bm_img: "",
bm_name: "", bm_name: "",
bm_pdf: "", bm_pdf: "",
pdf: [] id: 0,
pdf: [],
weight: 0,
}) })
/*版面修改*/ /*版面修改*/
const handleUpdateBm = (row) => { const handleUpdateBm = (row) => {
@ -324,20 +351,51 @@ const handleUpdateBm = (row) => {
Bmform.pdf = [{'url': row.bm_pdf, 'name': getFileNameFromUrl(row.bm_pdf)}]; Bmform.pdf = [{'url': row.bm_pdf, 'name': getFileNameFromUrl(row.bm_pdf)}];
koiDrawerBm.value.koiOpen(); koiDrawerBm.value.koiOpen();
} }
/*添加版面*/
const handleAddDate = (row) => {
console.log(row);
Bmform.bm_name = "";
Bmform.bm_img = "";
Bmform.bm_pdf = "";
Bmform.weight = 0;
Bmform.id = 0;
Bmform.pdf = [];
Bmform.date_id = row.id;
koiDrawerBm.value.koiOpen();
}
/*版面排序修改*/
const addSort = async (row) => {
//console.log(row);
try {
const res: any = await bmUpdate({id: row.id, weight: row.weight});
koiNoticeSuccess("修改成功!");
handleTreeList();
} catch (error) {
console.log(error);
koiNoticeError("数据修改失败,请刷新重试🌻");
}
/*版面修改*/ }
/*版面修改/添加*/
const handleConfirmBmDo = async () => { const handleConfirmBmDo = async () => {
confirmLoading.value = true; confirmLoading.value = true;
try { try {
const res: any = await bmUpdate(Bmform); if (Bmform.id == 0) {
console.log("菜单数据表格数据->", res.data); const res: any = await bmOneAdd(Bmform);
koiNoticeSuccess("修改成功!"); console.log("菜单数据表格数据->", res.data);
koiNoticeSuccess("添加成功!");
} else {
const res: any = await bmUpdate(Bmform);
console.log("菜单数据表格数据->", res.data);
koiNoticeSuccess("修改成功!");
}
confirmLoading.value = false; confirmLoading.value = false;
koiDrawerBm.value.koiQuickClose(); koiDrawerBm.value.koiQuickClose();
handleTreeList(); handleTreeList();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
koiNoticeError("数据查询失败,请刷新重试🌻"); koiNoticeError("数据修改失败,请刷新重试🌻");
} }
} }
const handleListPageSize = (size) => { const handleListPageSize = (size) => {
@ -364,6 +422,14 @@ const handleTreeList = async () => {
tableList.value = []; // tableList.value = []; //
const res: any = await bmList(pageNumber); const res: any = await bmList(pageNumber);
console.log("菜单数据表格数据->", res.data.data); console.log("菜单数据表格数据->", res.data.data);
res.data.data = res.data.data.map(item => {
return {
...item,
uuid:generateUUID(),
};
});
tableList.value = res.data.data; tableList.value = res.data.data;
loading.value = false; loading.value = false;
total.value = res.data.count; total.value = res.data.count;
@ -377,6 +443,12 @@ onMounted(() => {
// //
handleTreeList(); handleTreeList();
getTypelist(); getTypelist();
// let index = tabsStore.tabList.find(tab => tab.path.includes('/paper/article/update/'));
// console.log(index)
// while (typeof (index) !='undefined') {
// tabsStore.removeTab(index.path);
// index = tabsStore.tabList.find(tab => tab.path.includes('/paper/article/update/'));
// }
}); });
const rowClick = (row, column, e) => { const rowClick = (row, column, e) => {
@ -402,10 +474,7 @@ const isExpandAll = ref(false);
const handleAdd = () => { const handleAdd = () => {
router.push("/paper/add"); router.push("/paper/add");
}; };
/*添加版面*/
const handleAddDate = (row) => {
}
/** 修改 */ /** 修改 */
const handleUpdateDate = (row) => { const handleUpdateDate = (row) => {
console.log(row); console.log(row);
@ -449,14 +518,15 @@ const handleDeleteDate = (row: any) => {
koiMsgBox("您确认需要删除期刊[ " + row.datetime + " ]么?") koiMsgBox("您确认需要删除期刊[ " + row.datetime + " ]么?")
.then(async () => { .then(async () => {
try { try {
await dateDel({'id':id}); await dateDel({'id': id});
koiNoticeSuccess("删除成功!"); koiNoticeSuccess("删除成功!");
handleTreeList(); handleTreeList();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
}) })
.catch(() => {}); .catch(() => {
});
}; };
/** 删除版面*/ /** 删除版面*/
const handleDeleteBm = (row: any) => { const handleDeleteBm = (row: any) => {
@ -464,14 +534,15 @@ const handleDeleteBm = (row: any) => {
koiMsgBox("您确认需要删除版面[ " + row.datetime + " ]么?") koiMsgBox("您确认需要删除版面[ " + row.datetime + " ]么?")
.then(async () => { .then(async () => {
try { try {
await bmDel({'id':id}); await bmDel({'id': id});
koiNoticeSuccess("删除成功!"); koiNoticeSuccess("删除成功!");
handleTreeList(); handleTreeList();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
}) })
.catch(() => {}); .catch(() => {
});
}; };
/** 删除新闻*/ /** 删除新闻*/
const handleDeleteNews = (row: any) => { const handleDeleteNews = (row: any) => {
@ -479,16 +550,21 @@ const handleDeleteNews = (row: any) => {
koiMsgBox("您确认需要删除新闻[ " + row.datetime + " ]么?") koiMsgBox("您确认需要删除新闻[ " + row.datetime + " ]么?")
.then(async () => { .then(async () => {
try { try {
await newsDel({'id':id}); await newsDel({'id': id});
koiNoticeSuccess("删除成功!"); koiNoticeSuccess("删除成功!");
handleTreeList(); handleTreeList();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
}) })
.catch(() => {}); .catch(() => {
});
}; };
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
::v-deep(.center-input .el-input__inner ) {
text-align: center;
}
</style>

View File

@ -298,10 +298,10 @@ import {
koiMsgInfo koiMsgInfo
} from "@/utils/koi.ts"; } from "@/utils/koi.ts";
// @ts-ignore // @ts-ignore
import { listPage, getById, add, update, deleteById, batchDelete, updateStatus } from "@/api/system/user/index.ts"; //import { listPage, getById, add, update, deleteById, batchDelete, updateStatus } from "@/api/system/user/index.ts";
import { listNormalRole, assignUserRole } from "@/api/system/role/index.ts"; //import { listNormalRole, assignUserRole } from "@/api/system/role/index.ts";
// @ts-ignore // @ts-ignore
import { listDataByType } from "@/api/system/dict/data/index.ts"; //import { listDataByType } from "@/api/system/dict/data/index.ts";
import { koiDatePicker } from "@/utils/index.ts"; import { koiDatePicker } from "@/utils/index.ts";
// //

View File

@ -18,11 +18,11 @@ import path from "path";
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
const env = loadEnv(mode, process.cwd()); // 获取配置文件别名配置 const env = loadEnv(mode, process.cwd()); // 获取配置文件别名配置
return { return {
base: '/dist/', // 设置为您的二级目录名称
plugins: [ plugins: [
vue(), vue(),
Unocss(), Unocss(),
vueSetupExtend(), vueSetupExtend(),
viteCompression(),
ViteImageOptimizer(), ViteImageOptimizer(),
createSvgIconsPlugin({ createSvgIconsPlugin({
// 配置SVG图片 // 配置SVG图片