Pre Merge pull request !86 from Kenjjj201377/dev

This commit is contained in:
Kenjjj201377 2022-10-23 15:28:10 +00:00 committed by Gitee
commit 916198994d
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
58 changed files with 2140 additions and 410 deletions

View File

@ -18,7 +18,7 @@ import { zhCN, dateZhCN, NConfigProvider } from 'naive-ui'
import { GoAppProvider } from '@/components/GoAppProvider' import { GoAppProvider } from '@/components/GoAppProvider'
import { I18n } from '@/components/I18n' import { I18n } from '@/components/I18n'
import { useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks' import {useSystemInit, useDarkThemeHook, useThemeOverridesHook, useCode } from '@/hooks'
// //
const darkTheme = useDarkThemeHook() const darkTheme = useDarkThemeHook()
@ -28,4 +28,7 @@ const overridesTheme = useThemeOverridesHook()
// //
const hljsTheme = useCode() const hljsTheme = useCode()
//
useSystemInit()
</script> </script>

145
src/backend/ibackend.ts Normal file
View File

@ -0,0 +1,145 @@
/**
*
* - login
* - logout
* token - checkToken
* - projectList
* - updateProject
* - copyProject
* - uploadFile
* - getFileUrl
* MyResponseType
*/
import { IndexDbBackend } from "./indexdbbackend";
import { MockBackend } from "./mockbackend";
export interface MyResponseType {
code: number; // 状态200 表示接口调用成功参考HttpEnum
msg: string; // 提示信息,配合 data 和 code
data: any; // data = null 表示接口结果错误,错误原因放在 msg
}
export class MyResponse implements MyResponseType {
code: number = 200;
msg: string = "";
data: any = {};
}
/**
* IBackend
*
*/
export interface IBackend {
/**
* oss地址等
* @param data
*/
init(data:any):any
/**
*
* @param data {} .username .password
* @return MyResponseType
* .data
* token:{tokenValue:"", tokenName:""},
* userinfo:{nickname:"", username: "", id: 用户ID}
*
* 1 .code 200 .msg
* 2 .code=200 .data = null, msg
* .data
* setLocalStorage(GO_LOGIN_INFO_STORE, res.data)
*/
login(data:any):any
/**
*
*/
logout():any
/**
* Token是否有效
* @param data {tokenValue, tokenName}
* @return login()
*/
checkToken(data:any):any
/**
*
* @param data {} .page, .limit
* @return [] map
* id: projectId
* title:projectName
* release,
* label:remarks
* image:indexImage urlgetFileUrl
*/
projectList(data:any):any
/**
*
* @param data
* .projectName
* @return id ID
*/
createProject(data: any):any
/**
*
* @param data .projectId
* @return
id:projectId
projectName,
state: release,
remarks,
content
*/
fetchProject(data: any):any
/**
*
* @param data
* .projectId
* .projectName
* .release
* .content
* .object File
* .remarks
* @return
*/
updateProject(data: any):any
/**
*
* @param data
* .copyId ID
* .projectName
* @return id ID
*/
copyProject(data: any):any
/**
*
* @param data
* .projectId
* @return
*/
deleteProject(data: any):any
/**
*
* @param file File
* @param params Todo: 上传文件可带上项目ID和其他附加信息便
* @return .uri uri getFileUrl
*/
uploadFile(file: File, params: any):any
/**
* uploadFile
* @param uploadUri uri
* @return image.src 使
*/
getFileUrl(uploadUri:string):string
}
export const BackEndFactory = new IndexDbBackend();
// export const BackEndFactory = new MockBackend();

View File

@ -0,0 +1,147 @@
/**
* IndexDb
*/
const win: { [k: string]: any } = window || globalThis;
const indexedDB =
win.indexedDB || win.mozIndexedDB || win.webkitIndexedDB || win.msIndexedDB;
const dbs: { [k: string]: IDBDatabase } = {};
let databaseName: string;
let request: IDBOpenDBRequest;
interface AnyEvent {
[k: string]: any;
}
export interface TableOption {
storeName: string;
option: { [K: string]: any };
index: { [K: string]: any }[];
}
export const createDB = (
name: string,
version?: string,
options?: TableOption[],
) =>
new Promise<IDBDatabase>((resolve, reject) => {
if (!indexedDB) reject('浏览器不支持indexedDB');
databaseName = name;
if (dbs?.[name]) {
resolve(dbs[name]);
return;
}
request = indexedDB.open(name, version);
createTable(options)?.then((db: IDBDatabase) => resolve(db));
request.onsuccess = (event: AnyEvent) => {
// IDBDatabase
const db = event.target.result;
// 缓存起来
dbs[name] = db;
resolve(db);
};
request.onerror = (event: AnyEvent) => reject(event);
});
export const createTable = (options?: TableOption[]) => {
if (!options) return;
return new Promise<IDBDatabase>((resolve) => {
request.onupgradeneeded = (event: AnyEvent) => {
const db = event.target.result;
dbs[databaseName] = db;
for (const i in options) {
// 判断是否存在表
if (!db.objectStoreNames.contains(options[i].storeName)) {
const objectStore = db.createObjectStore(
options[i].storeName,
options[i].option,
);
for (const j of options[i].index) {
objectStore.createIndex(j.name, j.keyPath, {
unique: j.unique,
});
}
}
}
resolve(db);
};
});
};
const getTransaction = async (name: string, version?: string) => {
let db: IDBDatabase;
// 先从缓存获取
if (dbs[databaseName]) {
db = dbs[databaseName];
} else {
db = await createDB(databaseName, version);
}
return db.transaction(name, 'readwrite');
};
const getObjectStore = async (
name: string,
version?: string,
): Promise<IDBObjectStore> => {
const transaction = await getTransaction(name, version);
return transaction.objectStore(name);
};
const getStore = (name: string, type: string, data: any) =>
new Promise<IDBDatabase>((resolve) => {
getObjectStore(name).then((objectStore: IDBObjectStore | any) => {
const request = objectStore[type](data);
request.onsuccess = (event: AnyEvent) =>
resolve(event.target.result);
});
});
const findStore = (
name: string,
start: any,
end: any,
startInclude: any,
endInclude: any,
) =>
new Promise<IDBDatabase>((resolve, reject) => {
getObjectStore(name).then((objectStore: IDBObjectStore) => {
const request = objectStore.openCursor(
IDBKeyRange.bound(start, end, startInclude, endInclude),
);
request.onsuccess = (event: AnyEvent) =>
resolve(event.target.result);
request.onerror = (event: AnyEvent) => reject(event);
});
});
export interface DBSelect {
add: (data: any) => Promise<IDBDatabase>;
get: (data: any) => Promise<IDBDatabase>;
getAll: () => Promise<IDBDatabase>;
del: (data: any) => Promise<IDBDatabase>;
clear: (data: any) => Promise<IDBDatabase>;
put: (data: any) => Promise<IDBDatabase>;
find: (
start: any,
end: any,
startInclude: any,
endInclude: any,
) => Promise<IDBDatabase>;
}
// 获取一个store
export const onDBSelect = async (
name: string,
version: string
): Promise<DBSelect> => {
const add = (data: any) => getStore(name, 'add', data);
const get = (data: any) => getStore(name, 'get', data);
const getAll = () => getStore(name, 'getAll', null);
const del = (data: any) => getStore(name, 'delete', data);
const clear = (data: any) => getStore(name, 'clear', data);
const put = (data: any) => getStore(name, 'put', data);
const find = (start: any, end: any, startInclude: any, endInclude: any) =>
findStore(name, start, end, startInclude, endInclude);
const options: DBSelect = { add, get, getAll, clear, del, put, find };
getObjectStore(name, version);
return options;
};

View File

@ -0,0 +1,155 @@
import { MyResponse, IBackend } from './ibackend'
import { createDB, DBSelect, onDBSelect } from './indexdb/indexdb'
import { fileToUrl, fileToBlob } from "@/utils"
const PROJECT_TABLE = "project"
const IMAGE_TABLE = "image" // 保存图片未实现Todo
const DB_NAME = "goview"
const DB_VER = "1"
export class IndexDbBackend implements IBackend {
public async init(data: any) {
let rtn:MyResponse = new MyResponse;
const db:IDBDatabase = await createDB(DB_NAME, DB_VER, [
{
storeName: PROJECT_TABLE,
option: {
keyPath: "projectId", autoIncrement:true
},
index: [
{name: 'projectId', keyPath: "projectId", unique: true},
{name: 'projectName', keyPath: "projectName", unique: false},
{name: 'release', keyPath: "release", unique: false},
{name: 'remarks', keyPath: "remarks", unique: false},
{name: 'content', keyPath: "content", unique: false},
{name: 'indexImage', keyPath: "indexImage", unique: false}
]
}
])
return rtn;
}
public async login(data:any) {
let rtn:MyResponse = new MyResponse;
if(data.password == "123456" && data.username == "admin"){
rtn.data = {
token:{tokenValue:"mockToken", tokenName:"name"},
userinfo:{nickname:"nickname", username:data.username, id:1}
}
}else{
rtn.data = null
rtn.msg = "admin 和 123456"
}
return rtn;
}
public async logout() {
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async checkToken(data: any) {
let rtn:MyResponse = new MyResponse;
console.log("CheckToken: " + data.token)
rtn.data = {
token:{tokenValue:"mockToken", tokenName:"name"},
userinfo:{nickname:"nickname", username:data.username, id:1}
}
return rtn;
}
public async projectList(data:any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const r:any = await db.getAll()
rtn.data = []
r.map(function (item: any) {
let url = ""
if(item.indexImage){
const Url = URL || window.URL || window.webkitURL
url = Url.createObjectURL(item.indexImage)
}
rtn.data.push({
id: item.projectId,
title: item.projectName,
release: item.release == 1,
label:item.remarks,
image:url
})
})
return rtn;
}
public async createProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
rtn.data.id = await db.add({ projectName:data.projectName })
return rtn;
}
public async fetchProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const r:any = await db.get(parseInt(data.projectId))
rtn.data = {
id:r.projectId,
projectName: r.projectName,
state: r.release,
remarks: r.remarks,
content: r.content
}
return rtn;
}
public async updateProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const row:any = await db.get(parseInt(data.projectId))
if("content" in data) row.content = data.content
if("projectName" in data) row.projectName = data.projectName
if("release" in data) row.release = data.release
if("remarks" in data) row.remarks = data.remarks
if("object" in data) {
row.indexImage = await fileToBlob(data.object)
}
await db.put(row)
return rtn;
}
public async copyProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
const row:any = await db.get(parseInt(data.copyId))
rtn.data.id =await db.add({
projectName:data.projectName,
content:row.content,
indexImage:row.indexImage,
remarks:row.remarks
})
return rtn;
}
public async deleteProject(data: any){
let rtn:MyResponse = new MyResponse;
const db:DBSelect = await onDBSelect(PROJECT_TABLE, DB_VER)
await db.del(parseInt(data.projectId))
return rtn;
}
public async changeProjectRelease(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async uploadFile(data: File, params:any){
// Todo: 图片可以保存在表中
let rtn:MyResponse = new MyResponse;
rtn.data.uri = fileToUrl(data)
return rtn;
}
public getFileUrl(uploadUri:string){
return uploadUri;
}
}

130
src/backend/mockbackend.ts Normal file
View File

@ -0,0 +1,130 @@
import { MyResponse, IBackend } from './ibackend'
import { fileToUrl } from '@/utils'
/**
* MockBackend
*
*/
export class MockBackend implements IBackend {
public async init(data: any) {
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async login(data:any) {
let rtn:MyResponse = new MyResponse;
if(data.password == "123456" && data.username == "admin"){
rtn.data = {
token:{tokenValue:"mockToken", tokenName:"name"},
userinfo:{nickname:"nickname", username:data.username, id:1}
}
}else{
rtn.data = null
rtn.msg = "用户名或密码错误!"
}
return rtn;
}
public async logout() {
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async checkToken(data:any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async projectList(data:any){
let rtn:MyResponse = new MyResponse;
rtn.data =[
{
id: 1,
title: '假数据不可用',
release: true,
label: '官方案例'
},
{
id: 2,
title: '物料2-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 3,
title: '物料3-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 4,
title: '物料4-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 5,
title: '物料5-假数据不可用',
release: false,
label: '官方案例'
}
];
return rtn;
}
public async createProject(data: any){
let rtn:MyResponse = new MyResponse;
rtn.data.id = "newId"
return rtn;
}
public async fetchProject(data: any){
let rtn:MyResponse = new MyResponse;
rtn.data = {
id:data.projectId,
projectName: '假数据不可用',
indexImage:'',
state: 0,
remarks: '官方案例',
content: null
}
return rtn;
}
public async saveProject(data: object){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async updateProject(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async copyProject(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async deleteProject(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async changeProjectRelease(data: any){
let rtn:MyResponse = new MyResponse;
return rtn;
}
public async uploadFile(data: File, params:any){
let rtn:MyResponse = new MyResponse;
rtn.data.uri = fileToUrl(data)
return rtn;
}
public getFileUrl(uploadUri:string){
return uploadUri;
}
}

View File

@ -40,8 +40,9 @@ export enum MenuEnum {
UN_GROUP = 'unGroup', UN_GROUP = 'unGroup',
// 后退 // 后退
BACK = 'back', BACK = 'back',
// 前进
FORWORD = 'forward', FORWORD = 'forward',
// 保存
SAVE = 'save',
// 锁定 // 锁定
LOCK = 'lock', LOCK = 'lock',
// 解除锁定 // 解除锁定
@ -72,3 +73,15 @@ export enum MacKeyboard {
SHIFT_SOURCE_KEY = '⇧', SHIFT_SOURCE_KEY = '⇧',
ALT_SOURCE_KEY = '⌥' ALT_SOURCE_KEY = '⌥'
} }
// 同步状态枚举
export enum SyncEnum {
// 等待
PENDING,
// 开始
START,
// 成功
SUCCESS,
// 失败
FAILURE
}

View File

@ -2,11 +2,11 @@
* @description: * @description:
*/ */
export enum ResultEnum { export enum ResultEnum {
DATA_SUCCESS = 0,
SUCCESS = 200, SUCCESS = 200,
SERVER_ERROR = 500, SERVER_ERROR = 500,
SERVER_FORBIDDEN = 403, SERVER_FORBIDDEN = 403,
NOT_FOUND = 404, NOT_FOUND = 404,
TOKEN_OVERDUE = 886,
TIMEOUT = 60000 TIMEOUT = 60000
} }
@ -26,6 +26,12 @@ export enum RequestContentTypeEnum {
SQL = 1 SQL = 1
} }
// 头部
export enum RequestHttpHeaderEnum {
TOKEN = 'Token',
COOKIE = 'Cookie'
}
/** /**
* @description: * @description:
*/ */

View File

@ -20,10 +20,15 @@ export enum PageEnum {
//重定向 //重定向
REDIRECT = '/redirect', REDIRECT = '/redirect',
REDIRECT_NAME = 'Redirect', REDIRECT_NAME = 'Redirect',
// 未发布
REDIRECT_UN_PUBLISH = '/redirect/unPublish',
REDIRECT_UN_PUBLISH_NAME = 'redirect-un-publish',
// 重载
RELOAD = '/reload', RELOAD = '/reload',
RELOAD_NAME = 'Reload', RELOAD_NAME = 'Reload',
// 首页 // 首页
BASE_HOME = '/project', BASE_HOME = '/project',
BASE_HOME_NAME = 'Project', BASE_HOME_NAME = 'Project',

View File

@ -1,25 +1,255 @@
import { CreateComponentType, EventLife } from '@/packages/index.d' import { CreateComponentType, EventLife } from '@/packages/index.d'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { BackEndFactory } from '@/backend/ibackend'
import { reactive, toRef , watch, computed} from 'vue';
/**
*
*
const range = runtime.fn.selectComponents("饼图 柱状图")
const h = runtime.fn.getChartConfig(range, "hide")
runtime.fn.setChartConfig(range, "hide", !h)
001 id 2wolqibrx3c000
runtime.fn.setChartConfig("柱状图001", "dataset", {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":130}]})
runtime.fn.setChartConfig("#2wolqibrx3c000", "dataset", {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":230}]})
const c = runtime.fn.selectOneComponent("分组")
if(c){
console.log(runtime.fn.getChartConfig(c, "isGroup" ))
runtime.fn.setChartConfig(c, "hide", true)
}
exposed
defineExpose({ actionTest:actionTest })
actionTest
runtime.fn.callExposed("柱状图", "actionTest")
A MOUNTED status1 Watch = "0"
watch(()=>runtime.variables.status1, newValue => runtime.fn.setChartConfig(this, "hide", newValue == "0"))
B MOUNTED status1 Watch = "1"
watch(()=>runtime.variables.status1, newValue => runtime.fn.setChartConfig(this, "hide", newValue == "1"))
A B
if(runtime.variables.status1 == "0"){
runtime.variables.status1 = "1"
} else{
runtime.variables.status1 = "0"
}
A MOUNTED data1 Watch
watch(()=>runtime.datasets.data1,
newValue => runtime.fn.setChartConfig(this, "dataset", newValue))
B MOUNTED data1 Watch
watch(()=>runtime.datasets.data1,
newValue => runtime.fn.setChartConfig(this, "dataset", newValue))
datasets.data1A B
runtime.datasets.data1 = {"dimensions":["product","data1","data2"],"source":[{"product":"Mon","data1":120,"data2":230}]}
*
*/
// * 初始化
export const useSystemInit = async () => {
const res = await BackEndFactory.init({}) as any;
}
const getOneChartConfig = (component:any, configName:string, params?:any)=>{
let root = null
if(component.proxy.chartConfig) root = component.proxy.chartConfig
else if (component.proxy.groupData) root = component.proxy.groupData
// if(!root) return null
switch(configName){
case "hide":
return root.status.hide
break;
case "dataset":
return root.option.dataset
break;
case "isGroup":
return root.isGroup
break;
case "key":
return root.key
break;
case "attr":
return root.attr
break;
case "name":
return root.chartConfig.title
}
}
const setOneChartConfig = (component:any, configName:string, newValue:any, params?:any)=>{
let root = null
if(component.proxy.chartConfig) root = component.proxy.chartConfig
else if (component.proxy.groupData) root = component.proxy.groupData
switch(configName){
case "hide":
root.status.hide = newValue
break;
case "dataset":
root.option.dataset = newValue
break;
}
}
/**
* css selectors
* . #
* [name=] Todo
* #id
* .key Todo
* @param selectors
* @returns []
*/
const getComponentsBySelectors = (selectors:string):any[]=>{
// 返回:数组,可能多个
let rtn:any[] = []
const ar = selectors.split(" ")
for(let a of ar){
rtn = rtn.concat(getComponentsBySelector(a))
}
return rtn
}
const getComponentsBySelector = (selector:string):any[]=>{
// 返回:数组,可能多个
const rtn:any[] = []
if(selector.substring(0,1) == "#")
{
const key = selector.substring(1)
if(key in components){
return [components[key]]
}
return rtn
}
for (let key in components) {
if(getOneChartConfig(components[key], "name") == selector){
rtn.push(components[key])
}
}
return rtn
}
// 所有图表组件集合对象 // 所有图表组件集合对象
const components: { [K in string]?: any } = {} const components: { [K in string]?: any } = {}
const runtime = {
// 变量,管理各种状态
variables:reactive({}),
// 数据集
datasets:reactive({}),
// 组件列表 {}
components:components,
// 帮助类
fn:{
/**
*
* @param selectors string | component | [component]
* @return component null
*/
selectOneComponent:(selectors:any)=>{
const cList = runtime.fn.selectComponents(selectors)
if(cList.length > 0){
return cList[0]
}
return null
},
/**
*
* @param selectors string | component | [component]
* @return [component] []
*/
selectComponents:(selectors:any):any[]=>{
if(!selectors) return []
if(typeof selectors == "string") return getComponentsBySelectors(selectors)
if(Array.isArray(selectors)) return selectors
return [selectors]
},
/**
* 使
* @param selectors string | component | [component]
* @param configName
* @param params
* @returns
*/
getChartConfig:(selectors:any, configName:string, params?:any)=>{
const component:any = runtime.fn.selectOneComponent(selectors)
if(!component && !component.proxy) return null
return getOneChartConfig(component, configName, params)
},
/**
*
* @param selectors string | component | [component]
* @param configName
* @param newValue
* @param params
* @returns
*/
setChartConfig:(selectors:any, configName:string, newValue:any, params?:any)=>{
const cList:any[] = runtime.fn.selectComponents(selectors)
for(let c of cList){
if(!c && !c.proxy) return null
setOneChartConfig(c, configName, newValue, params)
}
},
/**
* 使 defineExpose
* @param selectors string | component | [component]
* @param action defineExpose
* @param params
* @returns
*/
callExposed:(selectors:any, action:string, params?:any)=>{
const cList:any[] = runtime.fn.selectComponents(selectors)
for(let c of cList){
if(!c && !c.exposed) return null
if(typeof c.exposed[action] == "function") c.exposed[action](params)
}
}
}
}
// 项目提供的npm 包变量 // 项目提供的npm 包变量
export const npmPkgs = { echarts } export const npmPkgs = { echarts, toRef , watch, computed, runtime }
export const useLifeHandler = (chartConfig: CreateComponentType) => { export const useLifeHandler = (chartConfig: CreateComponentType) => {
const events = chartConfig.events || {} const events = chartConfig.events || {}
// 生成生命周期事件 // 生成生命周期事件
const lifeEvents = { let lifeEvents = {
[EventLife.BEFORE_MOUNT](e: any) { [EventLife.BEFORE_MOUNT](e: any) {
// 存储组件 // 存储组件
components[chartConfig.id] = e.component components[chartConfig.id] = e.component
const fnStr = (events[EventLife.BEFORE_MOUNT] || '').trim() const fnStr = (events[EventLife.BEFORE_MOUNT] || '').trim()
generateFunc(fnStr, e) generateFunc(fnStr, e, e.component)
}, },
[EventLife.MOUNTED](e: any) { [EventLife.MOUNTED](e: any) {
const fnStr = (events[EventLife.MOUNTED] || '').trim() const fnStr = (events[EventLife.MOUNTED] || '').trim()
generateFunc(fnStr, e) generateFunc(fnStr, e, e.component)
}
}
// 遍历,按需侦听
for(let key in EventLife)
{
if(key != "BEFORE_MOUNT" && key != "MOUNTED"){
const k = EventLife[key]
const fnStr = (events[<EventLife>k] || '').trim()
if(fnStr){
lifeEvents[k] = (e:any) => {
const fnStr = (events[<EventLife>k] || '').trim()
generateFunc(fnStr, e, components[chartConfig.id])
}
}
} }
} }
return lifeEvents return lifeEvents
@ -30,7 +260,8 @@ export const useLifeHandler = (chartConfig: CreateComponentType) => {
* @param fnStr * @param fnStr
* @param e * @param e
*/ */
function generateFunc(fnStr: string, e: any) { function generateFunc(fnStr: string, e: any, component:any) {
if(fnStr == "") return
try { try {
// npmPkgs 便于拷贝 echarts 示例时设置option 的formatter等相关内容 // npmPkgs 便于拷贝 echarts 示例时设置option 的formatter等相关内容
Function(` Function(`
@ -40,7 +271,7 @@ function generateFunc(fnStr: string, e: any) {
const {${Object.keys(npmPkgs).join()}} = node_modules; const {${Object.keys(npmPkgs).join()}} = node_modules;
${fnStr} ${fnStr}
} }
)`)().bind(e?.component)(e, components, npmPkgs) )`)().bind(component)(e, components, npmPkgs)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }

View File

@ -11,6 +11,8 @@ const global = {
help: 'Help', help: 'Help',
contact: 'About Software', contact: 'About Software',
logout: 'Logout', logout: 'Logout',
logout_success: 'Logout success',
logout_failure: 'Logout Failed',
// system setting // system setting
sys_set: 'System Setting', sys_set: 'System Setting',
lang_set: 'Language Setting', lang_set: 'Language Setting',
@ -26,8 +28,14 @@ const global = {
r_more: 'More', r_more: 'More',
} }
const http = {
error_message: 'The interface is abnormal, please check the interface!',
token_overdue_message: 'Login expired, please log in again!'
}
export default { export default {
global, global,
http,
login, login,
project project
} }

View File

@ -2,6 +2,6 @@ export default {
desc: "Login", desc: "Login",
form_auto: "Sign in automatically", form_auto: "Sign in automatically",
form_button: "Login", form_button: "Login",
login_success: "Login success", login_success: "Login success!",
login_message: "Please complete the letter", login_message: "Please complete the letter!",
} }

View File

@ -1,6 +1,8 @@
export default { export default {
create_btn: 'Creat', create_btn: 'Creat',
create_tip: 'Please select a content for development', create_success: 'Creat Success!',
create_failure: 'Failed to create, please try again later',
create_tip: 'Please select a content for development!',
project: 'Project', project: 'Project',
my: 'My', my: 'My',
new_project: 'New Project', new_project: 'New Project',

View File

@ -11,6 +11,8 @@ const global = {
help: '帮助中心', help: '帮助中心',
contact: '关于软件', contact: '关于软件',
logout: '退出登录', logout: '退出登录',
logout_success: '退出成功!',
logout_failure: '退出失败!',
// 系统设置 // 系统设置
sys_set: '系统设置', sys_set: '系统设置',
lang_set: '语言设置', lang_set: '语言设置',
@ -18,16 +20,27 @@ const global = {
r_edit: '编辑', r_edit: '编辑',
r_preview: '预览', r_preview: '预览',
r_copy: '克隆', r_copy: '克隆',
r_copy_success: '克隆成功!',
r_rename: '重命名', r_rename: '重命名',
r_rename_success: '重命名成功!',
r_publish: '发布', r_publish: '发布',
r_publish_success: '成功发布!',
r_unpublish: '取消发布', r_unpublish: '取消发布',
r_unpublish_success: '取消成功!',
r_download: '下载', r_download: '下载',
r_delete: '删除', r_delete: '删除',
r_delete_success: '删除成功!',
r_more: '更多', r_more: '更多',
} }
const http = {
error_message: '获取数据失败,请稍后重试!',
token_overdue_message: '登录过期,请重新登录!'
}
export default { export default {
global, global,
http,
login, login,
project project
} }

View File

@ -2,6 +2,6 @@ export default {
desc: "登录", desc: "登录",
form_auto: "自动登录", form_auto: "自动登录",
form_button: "登录", form_button: "登录",
login_success: "登录成功",
login_message: "请填写完整信息", login_message: "请填写完整信息",
login_success: "登录成功!",
} }

View File

@ -1,6 +1,8 @@
export default { export default {
// aside // aside
create_btn: '新建', create_btn: '新建',
create_success: '新建成功!',
create_failure: '新建失败,请稍后重试!',
create_tip: '从哪里出发好呢?', create_tip: '从哪里出发好呢?',
project: '项目', project: '项目',
my: '我的', my: '我的',

View File

@ -96,6 +96,13 @@ export enum EventLife {
MOUNTED = 'vnodeMounted', MOUNTED = 'vnodeMounted',
// 渲染之前 // 渲染之前
BEFORE_MOUNT = 'vnodeBeforeMount', BEFORE_MOUNT = 'vnodeBeforeMount',
// 鼠标事件
MOUSE_CLICK = 'click',
MOUSE_OVER = "mouseover",
MOUSE_LEAVE = "mouseleave",
// 图表事件
ECHART_LEGEND_SELECT_CHANGED = "legendselectchanged",
ECHART_HIGH_LIGHT = "highlight"
} }
// 组件实例类 // 组件实例类

View File

@ -53,6 +53,7 @@ import {
ArrowForward as ArrowForwardIcon, ArrowForward as ArrowForwardIcon,
Planet as PawIcon, Planet as PawIcon,
Search as SearchIcon, Search as SearchIcon,
Reload as ReloadIcon,
ChevronUpOutline as ChevronUpOutlineIcon, ChevronUpOutline as ChevronUpOutlineIcon,
ChevronDownOutline as ChevronDownOutlineIcon, ChevronDownOutline as ChevronDownOutlineIcon,
Pulse as PulseIcon, Pulse as PulseIcon,
@ -91,6 +92,7 @@ import {
FitToScreen as FitToScreenIcon, FitToScreen as FitToScreenIcon,
FitToHeight as FitToHeightIcon, FitToHeight as FitToHeightIcon,
FitToWidth as FitToWidthIcon, FitToWidth as FitToWidthIcon,
Save as SaveIcon,
Carbon3DCursor as Carbon3DCursorIcon, Carbon3DCursor as Carbon3DCursorIcon,
Carbon3DSoftware as Carbon3DSoftwareIcon, Carbon3DSoftware as Carbon3DSoftwareIcon,
Filter as FilterIcon, Filter as FilterIcon,
@ -205,6 +207,8 @@ const ionicons5 = {
PawIcon, PawIcon,
// 搜索(放大镜) // 搜索(放大镜)
SearchIcon, SearchIcon,
// 加载
ReloadIcon,
// 过滤器 // 过滤器
FilterIcon, FilterIcon,
// 向上 // 向上
@ -270,6 +274,8 @@ const carbon = {
FitToScreenIcon, FitToScreenIcon,
FitToHeightIcon, FitToHeightIcon,
FitToWidthIcon, FitToWidthIcon,
// 保存
SaveIcon,
// 成组 // 成组
Carbon3DCursorIcon, Carbon3DCursorIcon,
// 解组 // 解组

View File

@ -1,13 +1,13 @@
import { RouteRecordRaw } from 'vue-router' import { RouteRecordRaw } from 'vue-router'
import type { AppRouteRecordRaw } from '@/router/types'; import type { AppRouteRecordRaw } from '@/router/types';
import { ErrorPage404, ErrorPage403, ErrorPage500, Layout } from '@/router/constant'; import { ErrorPage404, ErrorPage403, ErrorPage500, Layout, RedirectHome, RedirectUnPublish } from '@/router/constant';
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { GoReload } from '@/components/GoReload' import { GoReload } from '@/components/GoReload'
export const LoginRoute: RouteRecordRaw = { export const LoginRoute: RouteRecordRaw = {
path: '/login', path: PageEnum.BASE_LOGIN,
name: 'Login', name: PageEnum.BASE_LOGIN_NAME,
component: () => import('@/views/login/index.vue'), component: () => import('@/views/login/index.vue'),
meta: { meta: {
title: '登录', title: '登录',
@ -60,22 +60,21 @@ export const ReloadRoute: AppRouteRecordRaw = {
}, },
} }
export const RedirectRoute: AppRouteRecordRaw = { export const RedirectRoute: RouteRecordRaw[] = [
{
path: PageEnum.REDIRECT, path: PageEnum.REDIRECT,
name: PageEnum.REDIRECT_NAME, name: PageEnum.REDIRECT_NAME,
component: Layout, component: RedirectHome,
meta: { meta: {
title: PageEnum.REDIRECT_NAME, title: PageEnum.REDIRECT_NAME,
}, },
children: [ },
{ {
path: '/redirect/:path(.*)', path: PageEnum.REDIRECT_UN_PUBLISH,
name: PageEnum.REDIRECT_NAME, name: PageEnum.REDIRECT_UN_PUBLISH_NAME,
component: () => import('@/views/redirect/index.vue'), component: RedirectUnPublish,
meta: { meta: {
title: PageEnum.REDIRECT_NAME, title: PageEnum.REDIRECT_UN_PUBLISH_NAME,
hideBreadcrumb: true,
}, },
}, },
], ]
};

View File

@ -4,6 +4,10 @@ export const ErrorPage403 = () => import('@/views/exception/403.vue');
export const ErrorPage500 = () => import('@/views/exception/500.vue'); export const ErrorPage500 = () => import('@/views/exception/500.vue');
export const RedirectHome = () => import('@/views/redirect/index.vue');
export const RedirectUnPublish = () => import('@/views/redirect/UnPublish.vue');
export const Layout = () => import('@/layout/index.vue'); export const Layout = () => import('@/layout/index.vue');
export const ParentLayout = () => import('@/layout/parentLayout.vue'); export const ParentLayout = () => import('@/layout/parentLayout.vue');

View File

@ -1,9 +1,8 @@
import type { App } from 'vue' import type { App } from 'vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import { RedirectRoute } from '@/router/base'
import { createRouterGuards } from './router-guards' import { createRouterGuards } from './router-guards'
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { HttpErrorPage, LoginRoute, ReloadRoute } from '@/router/base' import { HttpErrorPage, LoginRoute, ReloadRoute, RedirectRoute } from '@/router/base'
import { Layout } from '@/router/constant' import { Layout } from '@/router/constant'
import modules from '@/router/modules' import modules from '@/router/modules'
@ -19,6 +18,7 @@ const RootRoute: Array<RouteRecordRaw> = [
}, },
children: [ children: [
...HttpErrorPage, ...HttpErrorPage,
...RedirectRoute,
modules.projectRoutes, modules.projectRoutes,
modules.chartRoutes, modules.chartRoutes,
modules.previewRoutes modules.previewRoutes
@ -27,7 +27,7 @@ const RootRoute: Array<RouteRecordRaw> = [
] ]
export const constantRouter: any[] = [LoginRoute, ...RootRoute, RedirectRoute, ReloadRoute]; export const constantRouter: any[] = [LoginRoute, ...RootRoute, ReloadRoute];
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(''), history: createWebHashHistory(''),

View File

@ -1,7 +1,15 @@
import { Router } from 'vue-router'; import { Router } from 'vue-router';
import { PageEnum } from '@/enums/pageEnum' import { PageEnum, PreviewEnum } from '@/enums/pageEnum'
import { loginCheck } from '@/utils' import { loginCheck } from '@/utils'
// 路由白名单
const routerAllowList = [
// 登录
PageEnum.BASE_LOGIN_NAME,
// 预览
PreviewEnum.CHART_PREVIEW_NAME
]
export function createRouterGuards(router: Router) { export function createRouterGuards(router: Router) {
// 前置 // 前置
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
@ -10,13 +18,13 @@ export function createRouterGuards(router: Router) {
const isErrorPage = router.getRoutes().findIndex((item) => item.name === to.name); const isErrorPage = router.getRoutes().findIndex((item) => item.name === to.name);
if (isErrorPage === -1) { if (isErrorPage === -1) {
next({ name: PageEnum.ERROR_PAGE_NAME_404 }) next({ name: PageEnum.ERROR_PAGE_NAME_404 })
return
} }
if (!loginCheck()) { // @ts-ignore
if (to.name === PageEnum.BASE_LOGIN_NAME) { if (!routerAllowList.includes(to.name) && !loginCheck()) {
next()
}
next({ name: PageEnum.BASE_LOGIN_NAME }) next({ name: PageEnum.BASE_LOGIN_NAME })
return
} }
next() next()
}) })

View File

@ -55,9 +55,12 @@ export const backgroundImageSize = 5
// 预览展示方式 // 预览展示方式
export const previewScaleType = PreviewScaleEnum.FIT export const previewScaleType = PreviewScaleEnum.FIT
// 数据请求间隔 // 数据请求间隔s
export const requestInterval = 30 export const requestInterval = 30
// 工作台自动保存间隔s
export const saveInterval = 30
// 数据请求间隔单位 // 数据请求间隔单位
export const requestIntervalUnit = RequestHttpIntervalEnum.SECOND export const requestIntervalUnit = RequestHttpIntervalEnum.SECOND

View File

@ -1,5 +1,6 @@
import { CreateComponentType, CreateComponentGroupType, FilterEnum } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType, FilterEnum } from '@/packages/index.d'
import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d' import { HistoryActionTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
import { SyncEnum } from '@/enums/editPageEnum'
import { import {
RequestHttpEnum, RequestHttpEnum,
RequestContentTypeEnum, RequestContentTypeEnum,
@ -12,6 +13,29 @@ import {
import { PreviewScaleEnum } from '@/enums/styleEnum' import { PreviewScaleEnum } from '@/enums/styleEnum'
import type { ChartColorsNameType, GlobalThemeJsonType } from '@/settings/chartThemes/index' import type { ChartColorsNameType, GlobalThemeJsonType } from '@/settings/chartThemes/index'
// 项目数据枚举
export enum ProjectInfoEnum {
// ID
PROJECT_ID = "projectId",
// 名称
PROJECT_NAME = 'projectName',
// 描述
REMARKS = 'remarks',
// 缩略图
THUMBNAIL= 'thumbnail',
// 是否公开发布
RELEASE = 'release'
}
// 项目数据
export type ProjectInfoType = {
[ProjectInfoEnum.PROJECT_ID]: string,
[ProjectInfoEnum.PROJECT_NAME]: string,
[ProjectInfoEnum.REMARKS]: string,
[ProjectInfoEnum.THUMBNAIL]: string,
[ProjectInfoEnum.RELEASE]: boolean
}
// 编辑画布属性 // 编辑画布属性
export enum EditCanvasTypeEnum { export enum EditCanvasTypeEnum {
EDIT_LAYOUT_DOM = 'editLayoutDom', EDIT_LAYOUT_DOM = 'editLayoutDom',
@ -20,12 +44,13 @@ export enum EditCanvasTypeEnum {
SCALE = 'scale', SCALE = 'scale',
USER_SCALE = 'userScale', USER_SCALE = 'userScale',
LOCK_SCALE = 'lockScale', LOCK_SCALE = 'lockScale',
SAVE_STATUS = 'saveStatus',
IS_CREATE = 'isCreate', IS_CREATE = 'isCreate',
IS_DRAG = 'isDrag', IS_DRAG = 'isDrag',
IS_SELECT = 'isSelect' IS_SELECT = 'isSelect'
} }
// 编辑区域 // 编辑区域(临时)
export type EditCanvasType = { export type EditCanvasType = {
// 编辑区域 DOM // 编辑区域 DOM
[EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null [EditCanvasTypeEnum.EDIT_LAYOUT_DOM]: HTMLElement | null
@ -42,11 +67,13 @@ export type EditCanvasType = {
[EditCanvasTypeEnum.IS_CREATE]: boolean [EditCanvasTypeEnum.IS_CREATE]: boolean
// 拖拽中 // 拖拽中
[EditCanvasTypeEnum.IS_DRAG]: boolean [EditCanvasTypeEnum.IS_DRAG]: boolean
// 保存状态
[EditCanvasTypeEnum.SAVE_STATUS]: SyncEnum
// 框选中 // 框选中
[EditCanvasTypeEnum.IS_SELECT]: boolean [EditCanvasTypeEnum.IS_SELECT]: boolean
} }
// 滤镜/背景色/宽高主题等 // 画布数据/滤镜/背景色/宽高主题等
export enum EditCanvasConfigEnum { export enum EditCanvasConfigEnum {
WIDTH = 'width', WIDTH = 'width',
HEIGHT = 'height', HEIGHT = 'height',
@ -58,7 +85,14 @@ export enum EditCanvasConfigEnum {
PREVIEW_SCALE_TYPE = 'previewScaleType' PREVIEW_SCALE_TYPE = 'previewScaleType'
} }
export interface EditCanvasConfigType { // 画布属性(需保存)
export type EditCanvasConfigType = {
// ID
[EditCanvasConfigEnum.PROJECT_ID]: string,
// 项目名称
[EditCanvasConfigEnum.PROJECT_NAME]: string,
// 项目描述
[EditCanvasConfigEnum.REMARKS]: string,
// 滤镜-启用 // 滤镜-启用
[FilterEnum.FILTERS_SHOW]: boolean [FilterEnum.FILTERS_SHOW]: boolean
// 滤镜-色相 // 滤镜-色相
@ -130,6 +164,7 @@ export type RecordChartType = {
// Store 枚举 // Store 枚举
export enum ChartEditStoreEnum { export enum ChartEditStoreEnum {
PROJECT_INFO = 'projectInfo',
EDIT_RANGE = 'editRange', EDIT_RANGE = 'editRange',
EDIT_CANVAS = 'editCanvas', EDIT_CANVAS = 'editCanvas',
RIGHT_MENU_SHOW = 'rightMenuShow', RIGHT_MENU_SHOW = 'rightMenuShow',
@ -180,6 +215,7 @@ export interface RequestConfigType extends RequestPublicConfigType {
// Store 类型 // Store 类型
export interface ChartEditStoreType { export interface ChartEditStoreType {
[ChartEditStoreEnum.PROJECT_INFO]: ProjectInfoType
[ChartEditStoreEnum.EDIT_CANVAS]: EditCanvasType [ChartEditStoreEnum.EDIT_CANVAS]: EditCanvasType
[ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType [ChartEditStoreEnum.EDIT_CANVAS_CONFIG]: EditCanvasConfigType
[ChartEditStoreEnum.RIGHT_MENU_SHOW]: boolean [ChartEditStoreEnum.RIGHT_MENU_SHOW]: boolean

View File

@ -10,14 +10,22 @@ import { requestInterval, previewScaleType, requestIntervalUnit } from '@/settin
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore' import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
// 全局设置 // 全局设置
import { useSettingStore } from '@/store/modules/settingStore/settingStore' import { useSettingStore } from '@/store/modules/settingStore/settingStore'
// 历史类型
import { HistoryActionTypeEnum, HistoryItemType, HistoryTargetTypeEnum } from '@/store/modules/chartHistoryStore/chartHistoryStore.d'
// 画布枚举
import { MenuEnum, SyncEnum } from '@/enums/editPageEnum'
import { import {
HistoryActionTypeEnum, getUUID,
HistoryItemType, loadingStart,
HistoryTargetTypeEnum loadingFinish,
} from '@/store/modules/chartHistoryStore/chartHistoryStore.d' loadingError,
import { MenuEnum } from '@/enums/editPageEnum' isString,
import { getUUID, loadingStart, loadingFinish, loadingError, isString, isArray } from '@/utils' isArray
} from '@/utils'
import { import {
ProjectInfoType,
ChartEditStoreEnum, ChartEditStoreEnum,
ChartEditStorage, ChartEditStorage,
ChartEditStoreType, ChartEditStoreType,
@ -36,6 +44,14 @@ const settingStore = useSettingStore()
export const useChartEditStore = defineStore({ export const useChartEditStore = defineStore({
id: 'useChartEditStore', id: 'useChartEditStore',
state: (): ChartEditStoreType => ({ state: (): ChartEditStoreType => ({
// 项目数据
projectInfo: {
projectId: '',
projectName: '',
remarks: '',
thumbnail: '',
release: false
},
// 画布属性 // 画布属性
editCanvas: { editCanvas: {
// 编辑区域 Dom // 编辑区域 Dom
@ -54,7 +70,9 @@ export const useChartEditStore = defineStore({
// 拖拽中 // 拖拽中
isDrag: false, isDrag: false,
// 框选中 // 框选中
isSelect: false isSelect: false,
// 同步中
saveStatus: SyncEnum.PENDING
}, },
// 右键菜单 // 右键菜单
rightMenuShow: false, rightMenuShow: false,
@ -131,6 +149,9 @@ export const useChartEditStore = defineStore({
componentList: [] componentList: []
}), }),
getters: { getters: {
getProjectInfo(): ProjectInfoType {
return this.projectInfo
},
getMousePosition(): MousePositionType { getMousePosition(): MousePositionType {
return this.mousePosition return this.mousePosition
}, },
@ -165,6 +186,10 @@ export const useChartEditStore = defineStore({
} }
}, },
actions: { actions: {
// * 设置 peojectInfo 数据项
setProjectInfo<T extends keyof ProjectInfoType, K extends ProjectInfoType[T]>(key: T, value: K) {
this.projectInfo[key] = value
},
// * 设置 editCanvas 数据项 // * 设置 editCanvas 数据项
setEditCanvas<T extends keyof EditCanvasType, K extends EditCanvasType[T]>(key: T, value: K) { setEditCanvas<T extends keyof EditCanvasType, K extends EditCanvasType[T]>(key: T, value: K) {
this.editCanvas[key] = value this.editCanvas[key] = value

View File

@ -1,3 +1,47 @@
/**
* * base64转file
* @param dataurl
* @param fileName
* @returns
*/
export const base64toFile = (dataurl: string, fileName: string) => {
let dataArr = dataurl.split(","),
mime = (dataArr as any[])[0].match(/:(.*?);/)[1],
bstr = atob(dataArr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], fileName, { type: mime });
}
/**
* * file转url
*/
export const fileToUrl = (file: File): string => {
const Url = URL || window.URL || window.webkitURL
const ImageUrl = Url.createObjectURL(file)
return ImageUrl
}
/**
* file转 blob
* @param { File } file
*/
export const fileToBlob = (file:File) =>{
return new Promise<Blob>(function (resolve, reject) {
let reader = new FileReader()
reader.readAsArrayBuffer(file)
reader.onload = function (e: ProgressEvent<FileReader>) {
if(e.target){
const blob = new Blob([<ArrayBuffer>e.target.result], { type: file.type });
resolve(blob);
}
}
})
}
/** /**
* * * *
* @param { File } file * @param { File } file

27
src/utils/http.ts Normal file
View File

@ -0,0 +1,27 @@
/**
* allowRoute
* @param MyResponse MyResponseType
* @return
*/
import { ResultEnum } from "@/enums/httpEnum"
import { PageEnum, ErrorPageNameMap } from "@/enums/pageEnum"
import { redirectErrorPage, routerTurnByName } from '@/utils'
export const httpErrorHandle = (MyResponse?:any, allowRoute:boolean = true) => {
if(MyResponse){
const {code, msg} = MyResponse
if (MyResponse.code === ResultEnum.TOKEN_OVERDUE) {
window['$message'].error(msg || window['$t']('http.token_overdue_message'))
if(allowRoute) routerTurnByName(PageEnum.BASE_LOGIN_NAME)
return
}
if (MyResponse.code != ResultEnum.SUCCESS) {
// 其他错误处理 Todo
if (ErrorPageNameMap.get(code) && allowRoute) {
redirectErrorPage(code)
}
}
}
window['$message'].error(window['$t']('http.error_message'))
}

View File

@ -7,3 +7,4 @@ export * from '@/utils/plugin'
export * from '@/utils/components' export * from '@/utils/components'
export * from '@/utils/type' export * from '@/utils/type'
export * from '@/utils/file' export * from '@/utils/file'
export * from '@/utils/http'

View File

@ -1,11 +1,11 @@
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ResultEnum } from '@/enums/httpEnum' import { ResultEnum, RequestHttpHeaderEnum } from '@/enums/httpEnum'
import { ErrorPageNameMap, PageEnum } from '@/enums/pageEnum' import { ErrorPageNameMap, PageEnum, PreviewEnum } from '@/enums/pageEnum'
import { docPath, giteeSourceCodePath } from '@/settings/pathConst' import { docPath, giteeSourceCodePath } from '@/settings/pathConst'
import { cryptoDecode } from './crypto'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { clearLocalStorage, getLocalStorage } from './storage' import { clearLocalStorage, getLocalStorage, clearCookie } from './storage'
import router from '@/router' import router from '@/router'
import { BackEndFactory } from '@/backend/ibackend'
/** /**
* * * *
@ -101,12 +101,21 @@ export const reloadRoutePage = () => {
} }
/** /**
* * 退 * * 退
*/ */
export const logout = () => { export const logout = async () => {
try {
const res = await BackEndFactory.logout() as any
if(res.code === ResultEnum.SUCCESS) {
window['$message'].success(window['$t']('global.logout_success'))
clearCookie(RequestHttpHeaderEnum.COOKIE)
clearLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE) clearLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE)
routerTurnByName(PageEnum.BASE_LOGIN_NAME) routerTurnByName(PageEnum.BASE_LOGIN_NAME)
} }
} catch (error) {
window['$message'].success(window['$t']('global.logout_failure'))
}
}
/** /**
* * * *
@ -137,7 +146,8 @@ export const openGiteeSourceCode = () => {
* @returns boolean * @returns boolean
*/ */
export const isPreview = () => { export const isPreview = () => {
return document.location.hash.includes('preview') return false
//return document.location.hash.includes('preview')
} }
/** /**
@ -153,6 +163,28 @@ export const fetchRouteParams = () => {
} }
} }
export const fetchRouteQuery = () => {
try {
const route = useRoute()
return route.query
} catch (error) {
window['$message'].warning('查询路由信息失败,请联系管理员!')
}
}
/**
* *
* @returns object
*/
export const fetchRouteParamsLocation = () => {
try {
return document.location.hash.split('/').pop() || ''
} catch (error) {
window['$message'].warning('查询路由信息失败,请联系管理员!')
return ''
}
}
/** /**
* * * *
* @param confirm * @param confirm
@ -162,19 +194,28 @@ export const goHome = () => {
} }
/** /**
* * login * *
* @return boolean * @return boolean
*/ */
export const loginCheck = () => { export const loginCheck = () => {
try { try {
const info = getLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE) const info = getLocalStorage(StorageEnum.GO_LOGIN_INFO_STORE)
if (!info) return false if (!info) return false
const decodeInfo = cryptoDecode(info) // 检查 Token ?
if (decodeInfo) { if(info.token && info.userinfo) return true
return true
}
return false return false
} catch (error) { } catch (error) {
return false return false
} }
} }
/**
* *
* @returns
*/
export const previewPath = (id?: string | number) => {
const { origin, pathname } = document.location
const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
const previewPath = `${origin}${pathname}${path}/${id || fetchRouteParamsLocation()}`
return previewPath
}

View File

@ -68,3 +68,41 @@ export const getSessionStorage: (k: string) => any = (k: string) => {
export const clearSessioStorage = (name: string) => { export const clearSessioStorage = (name: string) => {
window.sessionStorage.removeItem(name) window.sessionStorage.removeItem(name)
} }
/**
* * cookie
* @param name
* @param cvalue
* @param exdays
*/
export const setCookie = (name: string, cvalue: string, exdays: number) => {
const d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
const expires = "expires=" + d.toUTCString();
document.cookie = name + "=" + cvalue + "; " + expires;
}
/**
* * cookie
* @param cname
* @returns string
*/
export const getCookie = (cname: string) => {
const name = cname + "=";
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1);
if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
}
return "";
}
/**
* * cookie
* @param name
* @returns string
*/
export const clearCookie = (name: string) => {
setCookie(name, "", -1);
}

View File

@ -113,28 +113,7 @@ export const isMac = () => {
return /macintosh|mac os x/i.test(navigator.userAgent) return /macintosh|mac os x/i.test(navigator.userAgent)
} }
/**
* * file转url
*/
export const fileToUrl = (file: File): string => {
const Url = URL || window.URL || window.webkitURL
const ImageUrl = Url.createObjectURL(file)
return ImageUrl
}
/**
* * file转base64
*/
export const fileTobase64 = (file: File, callback: Function) => {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function (e: ProgressEvent<FileReader>) {
if (e.target) {
let base64 = e.target.result
callback(base64)
}
}
}
/** /**
* * * *

View File

@ -30,7 +30,7 @@
:onBeforeUpload="beforeUploadHandle" :onBeforeUpload="beforeUploadHandle"
> >
<n-upload-dragger> <n-upload-dragger>
<img v-if="canvasConfig.backgroundImage" class="upload-show" :src="canvasConfig.backgroundImage" alt="背景" /> <img v-if="canvasConfig.backgroundImage" class="upload-show" :src="BackEndFactory.getFileUrl(canvasConfig.backgroundImage)" alt="背景" />
<div class="upload-img" v-show="!canvasConfig.backgroundImage"> <div class="upload-img" v-show="!canvasConfig.backgroundImage">
<img src="@/assets/images/canvas/noImage.png" /> <img src="@/assets/images/canvas/noImage.png" />
<n-text class="upload-desc" depth="3"> <n-text class="upload-desc" depth="3">
@ -133,9 +133,12 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasConfigEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { StylesSetting } from '@/components/Pages/ChartItemSetting' import { StylesSetting } from '@/components/Pages/ChartItemSetting'
import { UploadCustomRequestOptions } from 'naive-ui' import { UploadCustomRequestOptions } from 'naive-ui'
import { fileToUrl, loadAsyncComponent } from '@/utils' import { fileToUrl, loadAsyncComponent, fetchRouteParamsLocation } from '@/utils'
import { PreviewScaleEnum } from '@/enums/styleEnum' import { PreviewScaleEnum } from '@/enums/styleEnum'
import { ResultEnum } from '@/enums/httpEnum'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { BackEndFactory } from '@/backend/ibackend'
const { ColorPaletteIcon } = icon.ionicons5 const { ColorPaletteIcon } = icon.ionicons5
const { ScaleIcon, FitToScreenIcon, FitToHeightIcon, FitToWidthIcon } = icon.carbon const { ScaleIcon, FitToScreenIcon, FitToHeightIcon, FitToWidthIcon } = icon.carbon
@ -268,11 +271,21 @@ const clearColor = () => {
// //
const customRequest = (options: UploadCustomRequestOptions) => { const customRequest = (options: UploadCustomRequestOptions) => {
const { file } = options const { file } = options
nextTick(() => { nextTick(async () => {
if (file.file) { if (file.file) {
const ImageUrl = fileToUrl(file.file) const uploadRes = await BackEndFactory.uploadFile(file.file, null) as any
chartEditStore.setEditCanvasConfig(EditCanvasConfigEnum.BACKGROUND_IMAGE, ImageUrl) if(uploadRes.code === ResultEnum.SUCCESS) {
chartEditStore.setEditCanvasConfig(EditCanvasConfigEnum.SELECT_COLOR, false) chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.BACKGROUND_IMAGE,
uploadRes.data.uri
)
chartEditStore.setEditCanvasConfig(
EditCanvasConfigEnum.SELECT_COLOR,
false
)
return
}
window['$message'].error('添加图片失败,请稍后重试!')
} else { } else {
window['$message'].error('添加图片失败,请稍后重试!') window['$message'].error('添加图片失败,请稍后重试!')
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<n-modal class="go-chart-data-request" v-model:show="modelShow" :mask-closable="false"> <n-modal class="go-chart-data-request" v-model:show="modelShow" :mask-closable="false" @esc="escHandler">
<n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px"> <n-card :bordered="false" role="dialog" size="small" aria-modal="true" style="width: 1000px; height: 800px">
<template #header></template> <template #header></template>
<template #header-extra> </template> <template #header-extra> </template>
@ -32,10 +32,12 @@ import { RequestContentTypeEnum } from '@/enums/httpEnum'
import { useTargetData } from '../../../hooks/useTargetData.hook' import { useTargetData } from '../../../hooks/useTargetData.hook'
import { RequestGlobalConfig } from './components/RequestGlobalConfig' import { RequestGlobalConfig } from './components/RequestGlobalConfig'
import { RequestTargetConfig } from './components/RequestTargetConfig' import { RequestTargetConfig } from './components/RequestTargetConfig'
import { useSync } from '@/views/chart/hooks/useSync.hook'
const emit = defineEmits(['update:modelShow', 'sendHandle']) const emit = defineEmits(['update:modelShow', 'sendHandle'])
const { targetData } = useTargetData() const { targetData } = useTargetData()
const { dataSyncUpdate } = useSync()
// //
const { chartConfig } = toRefs(targetData.value) const { chartConfig } = toRefs(targetData.value)
const { requestContentType } = toRefs(targetData.value.request) const { requestContentType } = toRefs(targetData.value.request)
@ -51,6 +53,10 @@ defineProps({
const closeHandle = () => { const closeHandle = () => {
emit('update:modelShow', false) emit('update:modelShow', false)
emit('sendHandle') emit('sendHandle')
dataSyncUpdate()
}
const escHandler = ()=>{
emit('update:modelShow', false)
} }
</script> </script>

View File

@ -167,7 +167,12 @@ const { DocumentTextIcon, ChevronDownIcon, PencilIcon } = icon.ionicons5
const EventLifeName = { const EventLifeName = {
[EventLife.BEFORE_MOUNT]: '渲染之前', [EventLife.BEFORE_MOUNT]: '渲染之前',
[EventLife.MOUNTED]: '渲染之后' [EventLife.MOUNTED]: '渲染之后',
[EventLife.MOUSE_CLICK] : '点击',
[EventLife.MOUSE_OVER] : "进入",
[EventLife.MOUSE_LEAVE] : "离开",
[EventLife.ECHART_LEGEND_SELECT_CHANGED] : "选择图例",
[EventLife.ECHART_HIGH_LIGHT] : "高亮"
} }
const EventLifeTip = { const EventLifeTip = {

View File

@ -1,11 +1,10 @@
<template> <template>
<div class="go-edit-bottom"> <div class="go-edit-bottom">
<n-space> <div class="go-flex-items-center">
<!-- 历史记录 -->
<edit-history></edit-history> <edit-history></edit-history>
<!-- CTRL按键触发展示 --> <n-divider vertical />
<n-text id="keyboard-dress-show" depth="3"></n-text> <edit-data-sync></edit-data-sync>
</n-space> </div>
<n-space class="bottom-ri"> <n-space class="bottom-ri">
<!-- 快捷键提示 --> <!-- 快捷键提示 -->
@ -25,7 +24,12 @@
<n-tooltip trigger="hover"> <n-tooltip trigger="hover">
<template #trigger> <template #trigger>
<n-button @click="lockHandle" text> <n-button @click="lockHandle" text>
<n-icon class="lock-icon" :class="{ color: lockScale }" size="18" :depth="2"> <n-icon
class="lock-icon"
:class="{ color: lockScale }"
size="18"
:depth="2"
>
<lock-closed-outline-icon v-if="lockScale"></lock-closed-outline-icon> <lock-closed-outline-icon v-if="lockScale"></lock-closed-outline-icon>
<lock-open-outline-icon v-else></lock-open-outline-icon> <lock-open-outline-icon v-else></lock-open-outline-icon>
</n-icon> </n-icon>
@ -55,7 +59,8 @@
import { reactive, ref, toRefs, watchEffect } from 'vue' import { reactive, ref, toRefs, watchEffect } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { EditHistory } from '../EditHistory/index' import { EditHistory } from '../EditHistory/index'
import EditShortcutKey from '../EditShortcutKey/index.vue' import { EditShortcutKey } from '../EditShortcutKey/index'
import { EditDataSync } from '../EditDataSync/index'
import { useDesignStore } from '@/store/modules/designStore/designStore' import { useDesignStore } from '@/store/modules/designStore/designStore'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
@ -136,12 +141,13 @@ watchEffect(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
$min-width: 500px; $min-width: 500px;
@include go('edit-bottom') { @include go('edit-bottom') {
width: 100%;
min-width: $min-width;
padding: 0 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0 10px;
width: 100%;
min-width: $min-width;
height: 40px;
.bottom-ri { .bottom-ri {
position: relative; position: relative;
top: 15px; top: 15px;

View File

@ -0,0 +1,3 @@
import EditDataSync from './index.vue'
export { EditDataSync }

View File

@ -0,0 +1,97 @@
<template>
<div class="go-edit-data-sync go-flex-items-center">
<n-tooltip trigger="hover">
<template #trigger>
<n-text class="status-desc go-ml-2" :type="descType" depth="3">
{{ statusDesc }}
</n-text>
</template>
<span>{{saveInterval}}s 更新一次</span>
</n-tooltip>
<n-spin
v-show="statusDesc === statusDescObj[1]['text']"
class="go-ml-2"
size="small"
>
<template #icon>
<n-icon size="13">
<reload-icon />
</n-icon>
</template>
</n-spin>
</div>
</template>
<script lang="ts" setup>
import { ref, toRefs, watch } from 'vue'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useDesignStore } from '@/store/modules/designStore/designStore'
import { SyncEnum } from '@/enums/editPageEnum'
import { icon } from '@/plugins'
import { saveInterval } from '@/settings/designSetting'
const { ReloadIcon } = icon.ionicons5
const chartEditStore = useChartEditStore()
const designStore = useDesignStore()
const { saveStatus } = toRefs(chartEditStore.getEditCanvas)
const themeColor = ref(designStore.getAppTheme)
const statusDesc = ref('')
const descType = ref('')
let setTimeoutIns: NodeJS.Timeout = setTimeout(() => {})
const statusDescObj = {
[SyncEnum.PENDING]: {
text: '等待自动同步',
type: '',
},
[SyncEnum.START]: {
text: '正在同步中',
type: 'success',
},
[SyncEnum.SUCCESS]: {
text: '同步成功!',
type: 'success',
},
[SyncEnum.FAILURE]: {
text: '同步失败!',
type: 'error',
},
}
watch(
() => saveStatus.value,
newData => {
clearTimeout(setTimeoutIns)
statusDesc.value = statusDescObj[newData]['text']
descType.value = statusDescObj[newData]['type']
// 3
setTimeoutIns = setTimeout(() => {
statusDesc.value = statusDescObj[SyncEnum.PENDING]['text']
descType.value = statusDescObj[SyncEnum.PENDING]['type']
}, 3000)
},
{
immediate: true,
}
)
</script>
<style lang="scss" scoped>
@include go('edit-data-sync') {
@include deep() {
.n-spin {
width: 13px;
height: 13px;
}
}
.status-desc {
cursor: default;
color: v-bind('themeColor');
font-size: 12px;
opacity: 0.8;
}
}
</style>

View File

@ -113,20 +113,25 @@ const shortcutKeyOptions = [
win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + Z `, win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + Z `,
mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z ` mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z `
}, },
{
label: '保存',
win: `${WinKeyboard.CTRL.toUpperCase()} + S `,
mac: `${MacKeyboard.CTRL.toUpperCase()} + S `,
},
{ {
label: '多选', label: '多选',
win: `${WinKeyboard.CTRL.toUpperCase()} + 🖱️ `, win: `${WinKeyboard.CTRL.toUpperCase()} + 🖱️ `,
mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + 🖱️ ` mac: `${MacKeyboard.CTRL.toUpperCase()} + 🖱️ `
}, },
{ {
label: '创建分组', label: '创建分组',
win: `${WinKeyboard.CTRL.toUpperCase()} + G / 🖱️ `, win: `${WinKeyboard.CTRL.toUpperCase()} + G / 🖱️ `,
mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + G / 🖱️` mac: `${MacKeyboard.CTRL.toUpperCase()} + G / 🖱️`
}, },
{ {
label: '解除分组', label: '解除分组',
win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G `, win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G `,
mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G ` mac: `${MacKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + G `
} }
] ]
const closeHandle = () => { const closeHandle = () => {

View File

@ -91,6 +91,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
import { useLayout } from './hooks/useLayout.hook' import { useLayout } from './hooks/useLayout.hook'
import { useAddKeyboard } from '../hooks/useKeyboard.hook' import { useAddKeyboard } from '../hooks/useKeyboard.hook'
import { useSync } from '../hooks/useSync.hook'
import { dragHandle, dragoverHandle, mousedownHandleUnStop, useMouseHandle } from './hooks/useDrag.hook' import { dragHandle, dragoverHandle, mousedownHandleUnStop, useMouseHandle } from './hooks/useDrag.hook'
import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook' import { useComponentStyle, useSizeStyle } from './hooks/useStyle.hook'
@ -101,9 +102,11 @@ import { EditRule } from './components/EditRule'
import { EditBottom } from './components/EditBottom' import { EditBottom } from './components/EditBottom'
import { EditShapeBox } from './components/EditShapeBox' import { EditShapeBox } from './components/EditShapeBox'
import { EditTools } from './components/EditTools' import { EditTools } from './components/EditTools'
import { BackEndFactory } from '@/backend/ibackend'
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const { handleContextMenu } = useContextMenu() const { handleContextMenu } = useContextMenu()
const { dataSyncFetch, intervalDataSyncUpdate } = useSync()
// //
useLayout() useLayout()
@ -156,7 +159,7 @@ const filterShow = computed(() => {
const rangeStyle = computed(() => { const rangeStyle = computed(() => {
// //
const background = chartEditStore.getEditCanvasConfig.background const background = chartEditStore.getEditCanvasConfig.background
const backgroundImage = chartEditStore.getEditCanvasConfig.backgroundImage const backgroundImage = BackEndFactory.getFileUrl(chartEditStore.getEditCanvasConfig.backgroundImage)
const selectColor = chartEditStore.getEditCanvasConfig.selectColor const selectColor = chartEditStore.getEditCanvasConfig.selectColor
const backgroundColor = background ? background : undefined const backgroundColor = background ? background : undefined
@ -172,9 +175,13 @@ const rangeStyle = computed(() => {
} }
}) })
//
onMounted(() => { onMounted(() => {
//
useAddKeyboard() useAddKeyboard()
//
dataSyncFetch()
//
intervalDataSyncUpdate()
}) })
</script> </script>

View File

@ -29,6 +29,23 @@
</template> </template>
<span>{{ item.title }}</span> <span>{{ item.title }}</span>
</n-tooltip> </n-tooltip>
<n-divider vertical />
<!-- 保存 -->
<n-tooltip placement="bottom" trigger="hover">
<template #trigger>
<div class="save-btn" >
<n-button size="small" type="primary" ghost @click="dataSyncUpdate()">
<template #icon>
<n-icon>
<SaveIcon></SaveIcon>
</n-icon>
</template>
</n-button>
</div>
</template>
<span>保存</span>
</n-tooltip>
</n-space> </n-space>
</n-space> </n-space>
</template> </template>
@ -38,7 +55,7 @@ import { toRefs, Ref, reactive, computed } from 'vue'
import { renderIcon, goDialog, goHome } from '@/utils' import { renderIcon, goDialog, goHome } from '@/utils'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { useRemoveKeyboard } from '../../hooks/useKeyboard.hook' import { useRemoveKeyboard } from '../../hooks/useKeyboard.hook'
import { useSync } from '../../hooks/useSync.hook'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore' import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
@ -48,7 +65,9 @@ import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayou
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d' import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
const { LayersIcon, BarChartIcon, PrismIcon, HomeIcon, ArrowBackIcon, ArrowForwardIcon } = icon.ionicons5 const { LayersIcon, BarChartIcon, PrismIcon, HomeIcon, ArrowBackIcon, ArrowForwardIcon } = icon.ionicons5
const { SaveIcon } = icon.carbon
const { setItem } = useChartLayoutStore() const { setItem } = useChartLayoutStore()
const { dataSyncUpdate } = useSync()
const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore()) const { getLayers, getCharts, getDetails } = toRefs(useChartLayoutStore())
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const chartHistoryStore = useChartHistoryStore() const chartHistoryStore = useChartHistoryStore()
@ -130,7 +149,7 @@ const clickHistoryHandle = (item: ItemType<HistoryStackEnum>) => {
// //
const goHomeHandle = () => { const goHomeHandle = () => {
goDialog({ goDialog({
message: '返回将不会保存任何操作', message: '确定已保存了数据Ctrl / ⌘ + S并返回到首页吗',
isMaskClosable: true, isMaskClosable: true,
onPositiveCallback: () => { onPositiveCallback: () => {
goHome() goHome()

View File

@ -1,29 +1,98 @@
<template> <template>
<n-space class="go-mt-0"> <n-space>
<n-button v-for="item in btnList" :key="item.title" ghost @click="item.event"> <n-button
v-for="item in btnList"
:key="item.key"
:type="item.type()"
ghost
@click="item.event"
>
<template #icon> <template #icon>
<component :is="item.icon"></component> <component :is="item.icon"></component>
</template> </template>
<span>{{ item.title }}</span> <span>{{ item.title() }}</span>
</n-button> </n-button>
</n-space> </n-space>
<!-- 发布管理弹窗 -->
<n-modal v-model:show="modelShow" @afterLeave="closeHandle">
<n-list bordered class="go-system-setting">
<template #header>
<n-space justify="space-between">
<n-h3 class="go-mb-0">发布管理</n-h3>
<n-icon size="20" class="go-cursor-pointer" @click="closeHandle">
<close-icon></close-icon>
</n-icon>
</n-space>
</template>
<n-list-item>
<n-space :size="10">
<n-alert :show-icon="false" title="预览地址:" type="success">
{{ previewPath() }}
</n-alert>
<n-space vertical>
<n-button tertiary type="primary" @click="copyPreviewPath()">
复制地址
</n-button>
<n-button :type="release ? 'warning' : 'primary'" @click="sendHandle">
{{ release ? '取消发布' : '发布大屏' }}
</n-button>
</n-space>
</n-space>
</n-list-item>
<n-list-item>
<n-space :size="10">
<n-button @click="modelShowHandle">关闭弹窗</n-button>
</n-space>
</n-list-item>
</n-list>
</n-modal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { shallowReactive } from 'vue' import { ref, shallowReactive, watchEffect } from 'vue'
import { renderIcon, goDialog, fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils' import { useRoute } from 'vue-router'
import { useClipboard } from '@vueuse/core'
import { PreviewEnum } from '@/enums/pageEnum' import { PreviewEnum } from '@/enums/pageEnum'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { useRoute } from 'vue-router' import { ResultEnum } from '@/enums/httpEnum'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' import { ProjectInfoEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { BackEndFactory } from '@/backend/ibackend'
import {
previewPath,
renderIcon,
fetchPathByName,
routerTurnByPath,
setSessionStorage,
getLocalStorage,
httpErrorHandle,
fetchRouteParamsLocation,
} from '@/utils'
import { icon } from '@/plugins' import { icon } from '@/plugins'
const { BrowsersOutlineIcon, SendIcon } = icon.ionicons5 const { BrowsersOutlineIcon, SendIcon, CloseIcon } = icon.ionicons5
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
const previewPathRef = ref(previewPath())
const { copy, isSupported } = useClipboard({ source: previewPathRef })
const routerParamsInfo = useRoute() const routerParamsInfo = useRoute()
const modelShow = ref<boolean>(false)
const release = ref<boolean>(false)
watchEffect(() => {
release.value = chartEditStore.getProjectInfo.release || false
})
//
const closeHandle = () => {
modelShow.value = false
}
// //
const previewHandle = () => { const previewHandle = () => {
const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href') const path = fetchPathByName(PreviewEnum.CHART_PREVIEW_NAME, 'href')
@ -32,55 +101,100 @@ const previewHandle = () => {
// id // id
const previewId = typeof id === 'string' ? id : id[0] const previewId = typeof id === 'string' ? id : id[0]
const storageInfo = chartEditStore.getStorageInfo const storageInfo = chartEditStore.getStorageInfo
const sessionStorageInfo = getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || [] const sessionStorageInfo =
getLocalStorage(StorageEnum.GO_CHART_STORAGE_LIST) || []
if (sessionStorageInfo?.length) { if (sessionStorageInfo?.length) {
const repeateIndex = sessionStorageInfo.findIndex((e: { id: string }) => e.id === previewId) const repeateIndex = sessionStorageInfo.findIndex(
(e: { id: string }) => e.id === previewId
)
// //
if (repeateIndex !== -1) { if (repeateIndex !== -1) {
sessionStorageInfo.splice(repeateIndex, 1, { id: previewId, ...storageInfo }) sessionStorageInfo.splice(repeateIndex, 1, {
id: previewId,
...storageInfo,
})
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
} else { } else {
sessionStorageInfo.push({ sessionStorageInfo.push({
id: previewId, ...storageInfo id: previewId,
...storageInfo,
}) })
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, sessionStorageInfo)
} }
} else { } else {
setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [{ id: previewId, ...storageInfo }]) setSessionStorage(StorageEnum.GO_CHART_STORAGE_LIST, [
{ id: previewId, ...storageInfo },
])
} }
// //
routerTurnByPath(path, [previewId], undefined, true) routerTurnByPath(path, [previewId], undefined, true)
} }
//
const modelShowHandle = () => {
modelShow.value = !modelShow.value
}
//
const copyPreviewPath = (successText?: string, failureText?: string) => {
if (isSupported) {
copy()
window['$message'].success(successText || '复制成功!')
} else {
window['$message'].error(failureText || '复制失败!')
}
}
// //
const sendHandle = () => { const sendHandle = async () => {
goDialog({ const res = (await BackEndFactory.updateProject({
message: '想体验发布功能,请前往 master-fetch 分支查看: https://gitee.com/MTrun/go-view/tree/master-fetch', projectId: fetchRouteParamsLocation(),
positiveText: '了然', //
closeNegativeText: true, release: release.value ? -1 : 1,
onPositiveCallback: () => {} })) as any
})
if (res.code === ResultEnum.SUCCESS) {
modelShowHandle()
if (!release.value) {
copyPreviewPath('发布成功!已复制地址到剪贴板~', '发布成功!')
} else {
window['$message'].success(`已取消发布`)
}
chartEditStore.setProjectInfo(ProjectInfoEnum.RELEASE, !release.value)
} else {
httpErrorHandle()
}
} }
const btnList = shallowReactive([ const btnList = shallowReactive([
{ {
select: true, key: 'preview',
title: '预览', title: () => '预览',
type: () => 'default',
icon: renderIcon(BrowsersOutlineIcon), icon: renderIcon(BrowsersOutlineIcon),
event: previewHandle event: previewHandle,
}, },
{ {
select: true, key: 'release',
title: '发布', title: () => (release.value ? '已发布' : '发布'),
icon: renderIcon(SendIcon), icon: renderIcon(SendIcon),
event: sendHandle type: () => (release.value ? 'primary' : 'default'),
} event: modelShowHandle,
},
]) ])
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.align-center { @include go('system-setting') {
margin-top: -4px; @extend .go-background-filter;
min-width: 100px;
max-width: 60vw;
padding-bottom: 20px;
@include deep() {
.n-list-item:not(:last-child) {
border-bottom: 0;
}
}
} }
</style> </style>

View File

@ -6,9 +6,7 @@
<n-text @click="handleFocus"> <n-text @click="handleFocus">
工作空间 - 工作空间 -
<n-button v-show="!focus" secondary round size="tiny"> <n-button v-show="!focus" secondary round size="tiny">
<span class="title"> <span class="title">{{ comTitle }}</span>
{{ comTitle }}
</span>
</n-button> </n-button>
</n-text> </n-text>
@ -29,31 +27,32 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, nextTick, computed } from 'vue' import { ref, nextTick, computed, watchEffect } from 'vue'
import { fetchRouteParams } from '@/utils' import { ResultEnum } from '@/enums/httpEnum'
import { fetchRouteParamsLocation, httpErrorHandle } from '@/utils'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ProjectInfoEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useSync } from '../../hooks/useSync.hook'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { BackEndFactory } from '@/backend/ibackend'
const chartEditStore = useChartEditStore()
const { dataSyncUpdate } = useSync()
const { FishIcon } = icon.ionicons5 const { FishIcon } = icon.ionicons5
const focus = ref<boolean>(false) const focus = ref<boolean>(false)
const inputInstRef = ref(null) const inputInstRef = ref(null)
// id const title = ref<string>(fetchRouteParamsLocation())
const fetchProhectInfoById = () => {
const routeParamsRes = fetchRouteParams()
if (!routeParamsRes) return
const { id } = routeParamsRes
if (id.length) {
return id[0]
}
return ''
}
const title = ref<string>(fetchProhectInfoById() || '') watchEffect(() => {
title.value = chartEditStore.getProjectInfo.projectName || ''
})
const comTitle = computed(() => { const comTitle = computed(() => {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties title.value = title.value && title.value.replace(/\s/g, '')
title.value = title.value.replace(/\s/g, '') return title.value.length ? title.value : fetchRouteParamsLocation()
return title.value.length ? title.value : '新项目'
}) })
const handleFocus = () => { const handleFocus = () => {
@ -63,8 +62,18 @@ const handleFocus = () => {
}) })
} }
const handleBlur = () => { const handleBlur = async () => {
focus.value = false focus.value = false
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_NAME, title.value || '')
const res = (await BackEndFactory.updateProject({
projectId: fetchRouteParamsLocation(),
projectName: title.value
})) as any
if (res.code === ResultEnum.SUCCESS) {
dataSyncUpdate()
} else {
httpErrorHandle()
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -5,6 +5,9 @@ import debounce from 'lodash/debounce'
import keymaster from 'keymaster' import keymaster from 'keymaster'
import { setKeyboardDressShow } from '@/utils' import { setKeyboardDressShow } from '@/utils'
import { useSync } from './useSync.hook'
const useSyncIns = useSync()
// Keymaster可以支持识别以下组合键shiftoptionaltctrlcontrolcommand和⌘ // Keymaster可以支持识别以下组合键shiftoptionaltctrlcontrolcommand和⌘
const chartEditStore = useChartEditStore() const chartEditStore = useChartEditStore()
@ -23,12 +26,13 @@ export const winKeyboardValue = {
[MenuEnum.DELETE]: 'delete', [MenuEnum.DELETE]: 'delete',
[MenuEnum.BACK]: winCtrlMerge('z'), [MenuEnum.BACK]: winCtrlMerge('z'),
[MenuEnum.FORWORD]: winCtrlMerge(winShiftMerge('z')), [MenuEnum.FORWORD]: winCtrlMerge(winShiftMerge('z')),
[MenuEnum.SAVE]: winCtrlMerge('s'),
[MenuEnum.GROUP]: winCtrlMerge('g'), [MenuEnum.GROUP]: winCtrlMerge('g'),
[MenuEnum.UN_GROUP]: winCtrlMerge(winShiftMerge('g')), [MenuEnum.UN_GROUP]: winCtrlMerge(winShiftMerge('g')),
[MenuEnum.LOCK]: winCtrlMerge('l'), [MenuEnum.LOCK]: winCtrlMerge('l'),
[MenuEnum.UNLOCK]: winCtrlMerge(winShiftMerge('l')), [MenuEnum.UNLOCK]: winCtrlMerge(winShiftMerge('l')),
[MenuEnum.HIDE]: winCtrlMerge('h'), [MenuEnum.HIDE]: winCtrlMerge('h'),
[MenuEnum.SHOW]: winCtrlMerge(winShiftMerge('h')) [MenuEnum.SHOW]: winCtrlMerge(winShiftMerge('h')),
} }
// 这个 Ctrl 后面还是换成了 ⌘ // 这个 Ctrl 后面还是换成了 ⌘
@ -48,6 +52,7 @@ export const macKeyboardValue = {
[MenuEnum.DELETE]: macCtrlMerge('backspace'), [MenuEnum.DELETE]: macCtrlMerge('backspace'),
[MenuEnum.BACK]: macCtrlMerge('z'), [MenuEnum.BACK]: macCtrlMerge('z'),
[MenuEnum.FORWORD]: macCtrlMerge(macShiftMerge('z')), [MenuEnum.FORWORD]: macCtrlMerge(macShiftMerge('z')),
[MenuEnum.SAVE]: macCtrlMerge('s'),
[MenuEnum.GROUP]: macCtrlMerge('g'), [MenuEnum.GROUP]: macCtrlMerge('g'),
[MenuEnum.UN_GROUP]: macCtrlMerge(macShiftMerge('g')), [MenuEnum.UN_GROUP]: macCtrlMerge(macShiftMerge('g')),
[MenuEnum.LOCK]: macCtrlMerge('l'), [MenuEnum.LOCK]: macCtrlMerge('l'),
@ -71,6 +76,7 @@ const winKeyList: Array<string> = [
winKeyboardValue.back, winKeyboardValue.back,
winKeyboardValue.forward, winKeyboardValue.forward,
winKeyboardValue.save,
winKeyboardValue.group, winKeyboardValue.group,
winKeyboardValue.unGroup, winKeyboardValue.unGroup,
@ -96,6 +102,7 @@ const macKeyList: Array<string> = [
macKeyboardValue.back, macKeyboardValue.back,
macKeyboardValue.forward, macKeyboardValue.forward,
macKeyboardValue.save,
macKeyboardValue.group, macKeyboardValue.group,
macKeyboardValue.unGroup, macKeyboardValue.unGroup,
@ -203,6 +210,11 @@ export const useAddKeyboard = () => {
case keyboardValue.show: case keyboardValue.show:
keymaster(e, throttle(() => { chartEditStore.setShow(); return false }, throttleTime)) keymaster(e, throttle(() => { chartEditStore.setShow(); return false }, throttleTime))
break; break;
// 保存 ct+s
case keyboardValue.save:
keymaster(e, throttle(() => { useSyncIns.dataSyncUpdate(); return false }, 200))
break;
} }
} }
winKeyList.forEach((key: string) => { winKeyList.forEach((key: string) => {

View File

@ -1,8 +1,19 @@
import { getUUID } from '@/utils' import { onUnmounted } from 'vue';
import html2canvas from 'html2canvas'
import { getUUID, httpErrorHandle, fetchRouteParamsLocation, base64toFile } from '@/utils'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ChartEditStoreEnum, ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { EditCanvasTypeEnum, ChartEditStoreEnum, ProjectInfoEnum, ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore' import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
//import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { fetchChartComponent, fetchConfigComponent, createComponent } from '@/packages/index' import { fetchChartComponent, fetchConfigComponent, createComponent } from '@/packages/index'
import { saveInterval } from '@/settings/designSetting'
import throttle from 'lodash/throttle'
// 接口状态
import { ResultEnum } from '@/enums/httpEnum'
// 接口
import { BackEndFactory } from '@/backend/ibackend'
// 画布枚举
import { SyncEnum } from '@/enums/editPageEnum'
import { CreateComponentType, CreateComponentGroupType, ConfigType } from '@/packages/index.d' import { CreateComponentType, CreateComponentGroupType, ConfigType } from '@/packages/index.d'
import { PublicGroupConfigClass } from '@/packages/public/publicConfig' import { PublicGroupConfigClass } from '@/packages/public/publicConfig'
import merge from 'lodash/merge' import merge from 'lodash/merge'
@ -130,7 +141,117 @@ export const useSync = () => {
} }
} }
/**
* *
* @param projectData
* @returns
*/
const updateStoreInfo = (projectData: {
id: string,
projectName: string,
indexImage: string,
remarks: string,
state: number
}) => {
const { id, projectName, remarks, indexImage, state } = projectData
// ID
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, id)
// 名称
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_NAME, projectName)
// 描述
chartEditStore.setProjectInfo(ProjectInfoEnum.REMARKS, remarks)
// 缩略图
chartEditStore.setProjectInfo(ProjectInfoEnum.THUMBNAIL, indexImage)
// 发布
chartEditStore.setProjectInfo(ProjectInfoEnum.RELEASE, state === 1)
}
// * 数据获取
const dataSyncFetch = async () => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
try {
const res = await BackEndFactory.fetchProject({ projectId: fetchRouteParamsLocation() }) as any
if (res.code === ResultEnum.SUCCESS) {
if (res.data) {
updateStoreInfo(res.data)
// 更新全局数据
if(res.data.content && res.data.content != "{}"){
await updateComponent(JSON.parse(res.data.content), true)
return
}
}
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, fetchRouteParamsLocation())
setTimeout(() => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
}, 1000)
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
} catch (error) {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
httpErrorHandle()
}
}
// * 数据保存
const dataSyncUpdate = throttle(async () => {
if(!fetchRouteParamsLocation()) return
let projectId = chartEditStore.getProjectInfo[ProjectInfoEnum.PROJECT_ID];
if(projectId === null || projectId === ''){
window['$message'].error('数据初未始化成功,请刷新页面!')
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
// 获取缩略图片
const range = document.querySelector('.go-edit-range') as HTMLElement
// 生成图片
const canvasImage: HTMLCanvasElement = await html2canvas(range, {
backgroundColor: null,
allowTaint: true,
useCORS: true
})
// 保存数据和预览图
const res= await BackEndFactory.updateProject({
projectId,
content:JSON.stringify(chartEditStore.getStorageInfo || {}),
object:base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`)
}) as any
if (res.code === ResultEnum.SUCCESS) {
// 成功状态
setTimeout(() => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
}, 1000)
return
}
//提示
window['$message'].success(window['$t']('global.r_save_fail'))
// 失败状态
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
}, 3000)
// * 定时处理
const intervalDataSyncUpdate = () => {
// 定时获取数据
const syncTiming = setInterval(() => {
dataSyncUpdate()
}, saveInterval * 1000)
// 销毁
onUnmounted(() => {
clearInterval(syncTiming)
})
}
return { return {
updateComponent updateComponent,
updateStoreInfo,
dataSyncFetch,
dataSyncUpdate,
intervalDataSyncUpdate
} }
} }

View File

@ -4,9 +4,9 @@
<img src="~@/assets/images/exception/500.svg" alt="" /> <img src="~@/assets/images/exception/500.svg" alt="" />
</div> </div>
<div class="text-center"> <div class="text-center">
<h1 class="text-base text-gray-500">抱歉服务器出错了</h1> <h1>抱歉服务器出错了</h1>
</div> </div>
<n-button type="primary" secondary @click="goHome">回到首页</n-button> <n-button type="primary" secondary @click="goLogin">重新登录</n-button>
</div> </div>
</template> </template>
@ -14,8 +14,8 @@
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { routerTurnByName } from '@/utils' import { routerTurnByName } from '@/utils'
function goHome() { function goLogin() {
routerTurnByName(PageEnum.BASE_HOME_NAME) routerTurnByName(PageEnum.BASE_LOGIN_NAME)
} }
</script> </script>

View File

@ -124,9 +124,13 @@ import { GoLangSelect } from '@/components/GoLangSelect'
import { LayoutHeader } from '@/layout/components/LayoutHeader' import { LayoutHeader } from '@/layout/components/LayoutHeader'
import { LayoutFooter } from '@/layout/components/LayoutFooter' import { LayoutFooter } from '@/layout/components/LayoutFooter'
import { PageEnum } from '@/enums/pageEnum' import { PageEnum } from '@/enums/pageEnum'
import { ResultEnum } from '@/enums/httpEnum'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { routerTurnByName, cryptoEncode, setLocalStorage } from '@/utils' import { routerTurnByName, cryptoEncode, setLocalStorage, clearLocalStorage } from '@/utils'
import { BackEndFactory } from '@/backend/ibackend'
const { GO_LOGIN_INFO_STORE } = StorageEnum const { GO_LOGIN_INFO_STORE } = StorageEnum
const { PersonOutlineIcon, LockClosedOutlineIcon } = icon.ionicons5 const { PersonOutlineIcon, LockClosedOutlineIcon } = icon.ionicons5
@ -210,17 +214,22 @@ const handleSubmit = (e: Event) => {
if (!errors) { if (!errors) {
const { username, password } = formInline const { username, password } = formInline
loading.value = true loading.value = true
setLocalStorage( clearLocalStorage(GO_LOGIN_INFO_STORE)
GO_LOGIN_INFO_STORE, const res = await BackEndFactory.login({
cryptoEncode(
JSON.stringify({
username, username,
password, password
}) }) as any
) loading.value = false
) if(res.code=== ResultEnum.SUCCESS && res.data) {
window['$message'].success(`${t('login.login_success')}!`) // const { tokenValue, tokenName } = res.data.token
// const { nickname, username, id } = res.data.userinfo
//
setLocalStorage( GO_LOGIN_INFO_STORE, res.data)
window['$message'].success(t('login.login_success'))
routerTurnByName(PageEnum.BASE_HOME_NAME, true) routerTurnByName(PageEnum.BASE_HOME_NAME, true)
}else{
window['$message'].error(res.msg ||`${t('login.login_error')}!`)
}
} else { } else {
window['$message'].error(`${t('login.login_message')}!`) window['$message'].error(`${t('login.login_message')}!`)
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<div <div
class="chart-item" class="chart-item"
v-for="(item, index) in localStorageInfo.componentList" v-for="(item, index) in reactiveList"
:class="animationsClass(item.styles.animations)" :class="animationsClass(item.styles.animations)"
:key="item.id" :key="item.id"
:style="{ :style="{
@ -19,6 +19,7 @@
:groupIndex="index" :groupIndex="index"
:themeSetting="themeSetting" :themeSetting="themeSetting"
:themeColor="themeColor" :themeColor="themeColor"
v-on="useLifeHandler(item)"
></preview-render-group> ></preview-render-group>
<!-- 单组件 --> <!-- 单组件 -->
@ -35,7 +36,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType, computed } from 'vue' import { PropType, computed, reactive } from 'vue'
import { ChartEditStorageType } from '../../index.d' import { ChartEditStorageType } from '../../index.d'
import { PreviewRenderGroup } from '../PreviewRenderGroup/index' import { PreviewRenderGroup } from '../PreviewRenderGroup/index'
import { CreateComponentGroupType } from '@/packages/index.d' import { CreateComponentGroupType } from '@/packages/index.d'
@ -50,6 +51,7 @@ const props = defineProps({
} }
}) })
const reactiveList = reactive(props.localStorageInfo.componentList)
// //
const themeSetting = computed(() => { const themeSetting = computed(() => {
const chartThemeSetting = props.localStorageInfo.editCanvasConfig.chartThemeSetting const chartThemeSetting = props.localStorageInfo.editCanvasConfig.chartThemeSetting

View File

@ -1,5 +1,6 @@
import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
export interface ChartEditStorageType extends ChartEditStorage { export interface ChartEditStorageType extends ChartEditStorage {
id: string id: string,
isRelease?: boolean
} }

View File

@ -1,91 +1,9 @@
<template> <template>
<div :class="`go-preview ${localStorageInfo.editCanvasConfig.previewScaleType}`"> <suspense>
<template v-if="showEntity"> <suspense-index></suspense-index>
<!-- 实体区域 --> </suspense>
<div ref="entityRef" class="go-preview-entity">
<!-- 缩放层 -->
<div ref="previewRef" class="go-preview-scale">
<!-- 展示层 -->
<div :style="previewRefStyle" v-if="show">
<!-- 渲染层 -->
<preview-render-list :localStorageInfo="localStorageInfo"></preview-render-list>
</div>
</div>
</div>
</template>
<template v-else>
<!-- 缩放层 -->
<div ref="previewRef" class="go-preview-scale">
<!-- 展示层 -->
<div :style="previewRefStyle" v-if="show">
<!-- 渲染层 -->
<preview-render-list :localStorageInfo="localStorageInfo"></preview-render-list>
</div>
</div>
</template>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import suspenseIndex from './suspenseIndex.vue'
import { PreviewRenderList } from './components/PreviewRenderList'
import { getFilterStyle } from '@/utils'
import { getEditCanvasConfigStyle, getSessionStorageInfo } from './utils'
import { useComInstall } from './hooks/useComInstall.hook'
import { useScale } from './hooks/useScale.hook'
import { useStore } from './hooks/useStore.hook'
import { PreviewScaleEnum } from '@/enums/styleEnum'
import type { ChartEditStorageType } from './index.d'
const localStorageInfo: ChartEditStorageType = getSessionStorageInfo() as ChartEditStorageType
const previewRefStyle = computed(() => {
return {
...getEditCanvasConfigStyle(localStorageInfo.editCanvasConfig),
...getFilterStyle(localStorageInfo.editCanvasConfig)
}
})
const showEntity = computed(() => {
const type = localStorageInfo.editCanvasConfig.previewScaleType
return type === PreviewScaleEnum.SCROLL_Y || type === PreviewScaleEnum.SCROLL_X
})
useStore(localStorageInfo)
const { entityRef, previewRef } = useScale(localStorageInfo)
const { show } = useComInstall(localStorageInfo)
</script> </script>
<style lang="scss" scoped>
@include go('preview') {
position: relative;
height: 100vh;
width: 100vw;
@include background-image('background-image');
&.fit,
&.full {
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.go-preview-scale {
transform-origin: center center;
}
}
&.scrollY {
overflow-x: hidden;
.go-preview-scale {
transform-origin: left top;
}
}
&.scrollX {
overflow-y: hidden;
.go-preview-scale {
transform-origin: left top;
}
}
.go-preview-entity {
overflow: hidden;
}
}
</style>

View File

@ -0,0 +1,110 @@
<template>
<div
:class="`go-preview ${localStorageInfo.editCanvasConfig.previewScaleType}`"
>
<template v-if="showEntity">
<!-- 实体区域 -->
<div ref="entityRef" class="go-preview-entity">
<!-- 缩放层 -->
<div ref="previewRef" class="go-preview-scale">
<!-- 展示层 -->
<div :style="previewRefStyle" v-if="show">
<!-- 渲染层 -->
<preview-render-list
:localStorageInfo="localStorageInfo"
></preview-render-list>
</div>
</div>
</div>
</template>
<template v-else>
<!-- 缩放层 -->
<div ref="previewRef" class="go-preview-scale">
<!-- 展示层 -->
<div :style="previewRefStyle" v-if="show">
<!-- 渲染层 -->
<preview-render-list
:localStorageInfo="localStorageInfo"
></preview-render-list>
</div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { PreviewRenderList } from './components/PreviewRenderList'
import { getFilterStyle, routerTurnByName, getSessionStorage } from '@/utils'
import { getEditCanvasConfigStyle, getSessionStorageInfo } from './utils'
import { PageEnum } from '@/enums/pageEnum'
import { StorageEnum } from '@/enums/storageEnum'
import { useScale } from './hooks/useScale.hook'
import { useStore } from './hooks/useStore.hook'
import { PreviewScaleEnum } from '@/enums/styleEnum'
import { useComInstall } from './hooks/useComInstall.hook'
import type { ChartEditStorageType } from './index.d'
const storageList: ChartEditStorageType[] = getSessionStorage(
StorageEnum.GO_CHART_STORAGE_LIST
)
const localStorageInfo = await getSessionStorageInfo() as ChartEditStorageType
// @ts-ignore
if(localStorageInfo.isRelease === false) {
routerTurnByName(PageEnum.REDIRECT_UN_PUBLISH_NAME, true, false)
}
const previewRefStyle = computed(() => {
return {
...getEditCanvasConfigStyle(localStorageInfo.editCanvasConfig),
...getFilterStyle(localStorageInfo.editCanvasConfig.filterShow ? localStorageInfo.editCanvasConfig : undefined),
}
})
const showEntity = computed(() => {
const type = localStorageInfo.editCanvasConfig.previewScaleType
return (
type === PreviewScaleEnum.SCROLL_Y || type === PreviewScaleEnum.SCROLL_X
)
})
useStore(localStorageInfo)
const { entityRef, previewRef } = useScale(localStorageInfo)
const { show } = useComInstall(localStorageInfo)
</script>
<style lang="scss" scoped>
@include go('preview') {
position: relative;
height: 100vh;
width: 100vw;
@include background-image('background-image');
&.fit,
&.full {
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.go-preview-scale {
transform-origin: center center;
}
}
&.scrollY {
overflow-x: hidden;
.go-preview-scale {
transform-origin: left top;
}
}
&.scrollX {
overflow-y: hidden;
.go-preview-scale {
transform-origin: left top;
}
}
.go-preview-entity {
overflow: hidden;
}
}
</style>

View File

@ -1,23 +1,55 @@
import { getSessionStorage } from '@/utils' import { loginCheck, getSessionStorage, fetchRouteParamsLocation, httpErrorHandle, fetchRouteParams, fetchRouteQuery, setLocalStorage } from '@/utils'
import { ResultEnum } from '@/enums/httpEnum'
import { StorageEnum } from '@/enums/storageEnum' import { StorageEnum } from '@/enums/storageEnum'
import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d' import { ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
import { BackEndFactory } from '@/backend/ibackend'
export interface ChartEditStorageType extends ChartEditStorage { export interface ChartEditStorageType extends ChartEditStorage {
id: string id: string
} }
// 根据路由 id 获取存储数据的信息 // 根据路由 id 获取存储数据的信息
export const getSessionStorageInfo = () => { export const getSessionStorageInfo = async () => {
const urlHash = document.location.hash const id = fetchRouteParamsLocation()
const toPathArray = urlHash.split('/')
const id = toPathArray && toPathArray[toPathArray.length - 1]
const storageList: ChartEditStorageType[] = getSessionStorage( const storageList: ChartEditStorageType[] = getSessionStorage(
StorageEnum.GO_CHART_STORAGE_LIST StorageEnum.GO_CHART_STORAGE_LIST
) )
if(!storageList) return // 是否本地预览
if (!storageList || storageList.findIndex(e => e.id === id.toString()) === -1) {
// 处理 Token 注入
const q = fetchRouteQuery();
if(q && q.token && !loginCheck()){
// Token 注入
const rt = await BackEndFactory.checkToken({ token: q.token }) as any
if (rt.code === ResultEnum.SUCCESS && rt.data) {
// 记录登陆信息
setLocalStorage( StorageEnum.GO_LOGIN_INFO_STORE, rt.data)
}else{
httpErrorHandle()
return {}
}
}
// 接口调用
const res = await BackEndFactory.fetchProject({ projectId: id }) as any
if (res.code === ResultEnum.SUCCESS && res.data) {
const { content, state } = res.data
if (state === -1) {
// 跳转未发布页
return { isRelease: false }
}
return { ...JSON.parse(content), id }
} else {
httpErrorHandle()
// 错误处理Todo
return {}
}
}
// 本地读取
for (let i = 0; i < storageList.length; i++) { for (let i = 0; i < storageList.length; i++) {
if (id.toString() === storageList[i]['id']) { if (id.toString() === storageList[i]['id']) {
return storageList[i] return storageList[i]

View File

@ -7,7 +7,7 @@
<mac-os-control-btn <mac-os-control-btn
class="top-btn" class="top-btn"
:hidden="['remove']" :hidden="['remove']"
@close="deleteHanlde" @close="deleteHandle"
@resize="resizeHandle" @resize="resizeHandle"
></mac-os-control-btn> ></mac-os-control-btn>
</div> </div>
@ -17,9 +17,7 @@
object-fit="contain" object-fit="contain"
height="180" height="180"
preview-disabled preview-disabled
:src=" :src="`${cardData.image}`"
requireUrl('project/moke-20211219181327.png')
"
:alt="cardData.title" :alt="cardData.title"
:fallback-src="requireErrorImg()" :fallback-src="requireErrorImg()"
></n-image> ></n-image>
@ -27,8 +25,8 @@
</div> </div>
<template #action> <template #action>
<div class="go-flex-items-center list-footer" justify="space-between"> <div class="go-flex-items-center list-footer" justify="space-between">
<n-text class="go-ellipsis-1" :title="cardData.title"> <n-text class="go-ellipsis-1">
{{ cardData.title || '' }} {{ cardData.title || cardData.id || '未命名' }}
</n-text> </n-text>
<!-- 工具 --> <!-- 工具 -->
<div class="go-flex-items-center list-footer-ri"> <div class="go-flex-items-center list-footer-ri">
@ -75,9 +73,9 @@
</n-tooltip> </n-tooltip>
</template> </template>
</n-space> </n-space>
</div>
<!-- end --> <!-- end -->
</div> </div>
</div>
</template> </template>
</n-card> </n-card>
</div> </div>
@ -100,17 +98,12 @@ const {
SendIcon SendIcon
} = icon.ionicons5 } = icon.ionicons5
const emit = defineEmits(['delete', 'resize', 'edit']) const emit = defineEmits(['preview', 'delete', 'resize', 'edit','copy', 'release'])
const props = defineProps({ const props = defineProps({
cardData: Object as PropType<Chartype> cardData: Object as PropType<Chartype>
}) })
// url
const requireUrl = (name: string) => {
return new URL(`../../../../../assets/images/${name}`, import.meta.url).href
}
const fnBtnList = reactive([ const fnBtnList = reactive([
{ {
label: renderLang('global.r_edit'), label: renderLang('global.r_edit'),
@ -133,12 +126,13 @@ const selectOptions = ref([
{ {
label: renderLang('global.r_copy'), label: renderLang('global.r_copy'),
key: 'copy', key: 'copy',
icon: renderIcon(CopyIcon) icon: renderIcon(CopyIcon),
}, },
{ {
label: renderLang('global.r_rename'), label: renderLang('global.r_rename'),
key: 'rename', key: 'rename',
icon: renderIcon(PencilIcon) icon: renderIcon(PencilIcon),
disabled: true
}, },
{ {
type: 'divider', type: 'divider',
@ -148,13 +142,14 @@ const selectOptions = ref([
label: props.cardData?.release label: props.cardData?.release
? renderLang('global.r_unpublish') ? renderLang('global.r_unpublish')
: renderLang('global.r_publish'), : renderLang('global.r_publish'),
key: 'send', key: 'release',
icon: renderIcon(SendIcon) icon: renderIcon(SendIcon)
}, },
{ {
label: renderLang('global.r_download'), label: renderLang('global.r_download'),
key: 'download', key: 'download',
icon: renderIcon(DownloadIcon) icon: renderIcon(DownloadIcon),
disabled: true
}, },
{ {
type: 'divider', type: 'divider',
@ -169,8 +164,17 @@ const selectOptions = ref([
const handleSelect = (key: string) => { const handleSelect = (key: string) => {
switch (key) { switch (key) {
case 'preview':
previewHandle()
break
case 'delete': case 'delete':
deleteHanlde() deleteHandle()
break
case 'copy':
emit('copy', props.cardData)
break;
case 'release':
releaseHandle()
break break
case 'edit': case 'edit':
editHandle() editHandle()
@ -178,8 +182,13 @@ const handleSelect = (key: string) => {
} }
} }
//
const previewHandle = () => {
emit('preview', props.cardData)
}
// //
const deleteHanlde = () => { const deleteHandle = () => {
emit('delete', props.cardData) emit('delete', props.cardData)
} }
@ -188,6 +197,11 @@ const editHandle = () => {
emit('edit', props.cardData) emit('edit', props.cardData)
} }
//
const releaseHandle = () => {
emit('release', props.cardData)
}
// //
const resizeHandle = () => { const resizeHandle = () => {
emit('resize', props.cardData) emit('resize', props.cardData)

View File

@ -1,58 +1,131 @@
import { ref } from 'vue' import { ref, reactive } from 'vue';
import { goDialog } from '@/utils' import { goDialog, httpErrorHandle } from '@/utils'
import { DialogEnum } from '@/enums/pluginEnum' import { DialogEnum } from '@/enums/pluginEnum'
import { ChartList } from '../../..' import { BackEndFactory } from '@/backend/ibackend'
import { Chartype, ChartList } from '../../../index.d'
import { ResultEnum } from '@/enums/httpEnum'
// 数据初始化 // 数据初始化
export const useDataListInit = () => { export const useDataListInit = () => {
const list = ref<ChartList>([
{
id: 1,
title: '物料1-假数据不可用',
release: true,
label: '官方案例'
},
{
id: 2,
title: '物料2-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 3,
title: '物料3-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 4,
title: '物料4-假数据不可用',
release: false,
label: '官方案例'
},
{
id: 5,
title: '物料5-假数据不可用',
release: false,
label: '官方案例'
}
])
// 删除 const loading = ref(true)
const deleteHandle = (cardData: object, index: number) => {
const paginat = reactive({
// 当前页数
page: 1,
// 每页值
limit: 12,
// 总数
count: 10,
})
const list = ref<ChartList>([])
// 数据请求
const fetchList = async () => {
loading.value = true
const res = await BackEndFactory.projectList({
page: paginat.page,
limit: paginat.limit
}) as any
if (res.code==ResultEnum.SUCCESS) {
const { count } = res
paginat.count = count
list.value = res.data;
setTimeout(() => {
loading.value = false
}, 500)
return
}
httpErrorHandle()
}
// 修改页数
const changePage = (_page: number) => {
paginat.page = _page
fetchList()
}
// 修改大小
const changeSize = (_size: number) => {
paginat.limit = _size
fetchList()
}
// 删除处理
const deleteHandle = (cardData: Chartype) => {
goDialog({ goDialog({
type: DialogEnum.DELETE, type: DialogEnum.DELETE,
promise: true, promise: true,
onPositiveCallback: () => onPositiveCallback: () => new Promise(res => {
new Promise(res => setTimeout(() => res(1), 1000)), res(BackEndFactory.deleteProject({
promiseResCallback: (e: any) => { projectId: cardData.id
window.$message.success('删除成功') }))
list.value.splice(index, 1) }),
promiseResCallback: (res: any) => {
if (res.code === ResultEnum.SUCCESS) {
window['$message'].success(window['$t']('global.r_delete_success'))
fetchList()
return
}
httpErrorHandle()
} }
}) })
} }
// 复制项目
const copyHandle = async (cardData: Chartype) => {
const { id, title } = cardData
const res = await BackEndFactory.copyProject({
copyId: id,
projectName: '复制-' + title
}) as any
if (res.code === ResultEnum.SUCCESS) {
list.value = []
fetchList()
window['$message'].success("复制项目成功!")
return
}
httpErrorHandle()
}
// 发布处理
const releaseHandle = async (cardData: Chartype, index: number) => {
const { id, release } = cardData
const res = await BackEndFactory.updateProject({
projectId: id,
// [-1未发布, 1发布]
release: !release ? 1 : -1
}) as any
if (res.code === ResultEnum.SUCCESS) {
list.value = []
fetchList()
// 发布 -> 未发布
if (release) {
window['$message'].success(window['$t']('global.r_unpublish_success'))
return
}
// 未发布 -> 发布
window['$message'].success(window['$t']('global.r_publish_success'))
return
}
httpErrorHandle()
}
// 立即请求
fetchList()
return { return {
loading,
paginat,
list, list,
fetchList,
copyHandle,
releaseHandle,
changeSize,
changePage,
deleteHandle deleteHandle
} }
} }

View File

@ -1,7 +1,7 @@
import { ref, Ref } from 'vue' import { ref } from 'vue'
import { ChartEnum } from '@/enums/pageEnum' import { ChartEnum } from '@/enums/pageEnum'
import { fetchPathByName, routerTurnByPath } from '@/utils' import { fetchPathByName, routerTurnByPath, openNewWindow, previewPath } from '@/utils'
import { Chartype } from '../../..' import { Chartype } from '../../../index.d'
export const useModalDataInit = () => { export const useModalDataInit = () => {
const modalShow = ref<boolean>(false) const modalShow = ref<boolean>(false)
const modalData = ref<Chartype | null>(null) const modalData = ref<Chartype | null>(null)
@ -12,25 +12,31 @@ export const useModalDataInit = () => {
modalData.value = null modalData.value = null
} }
// 打开 modal // 缩放处理
const resizeHandle = (cardData: Chartype) => { const resizeHandle = (cardData: Chartype) => {
if (!cardData) return if (!cardData) return
modalShow.value = true modalShow.value = true
modalData.value = cardData modalData.value = cardData
} }
// 打开 modal // 编辑处理
const editHandle = (cardData: Chartype) => { const editHandle = (cardData: Chartype) => {
if (!cardData) return if (!cardData) return
const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href') const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href')
routerTurnByPath(path, [cardData.id], undefined, true) routerTurnByPath(path, [cardData.id], undefined, true)
} }
// 预览处理
const previewHandle = (cardData: Chartype) => {
openNewWindow(previewPath(cardData.id))
}
return { return {
modalData, modalData,
modalShow, modalShow,
closeModal, closeModal,
resizeHandle, resizeHandle,
editHandle editHandle,
previewHandle
} }
} }

View File

@ -1,28 +1,41 @@
<template> <template>
<div class="go-items-list"> <div class="go-items-list">
<n-grid <!-- 加载 -->
:x-gap="20" <div v-show="loading">
:y-gap="20" <go-loading></go-loading>
cols="2 s:2 m:3 l:4 xl:4 xxl:4" </div>
responsive="screen" <!-- 列表 -->
> <div v-show="!loading">
<n-grid :x-gap="20" :y-gap="20" cols="2 s:2 m:3 l:4 xl:4 xxl:4" responsive="screen">
<n-grid-item v-for="(item, index) in list" :key="item.id"> <n-grid-item v-for="(item, index) in list" :key="item.id">
<project-items-card <project-items-card
:cardData="item" :cardData="item"
@preview="previewHandle"
@resize="resizeHandle" @resize="resizeHandle"
@delete="deleteHandle($event, index)" @delete="deleteHandle(item)"
@release="releaseHandle(item, index)"
@copy="copyHandle(item)"
@edit="editHandle" @edit="editHandle"
></project-items-card> ></project-items-card>
</n-grid-item> </n-grid-item>
</n-grid> </n-grid>
</div>
<!-- 分页 -->
<div class="list-pagination"> <div class="list-pagination">
<n-pagination <n-pagination
:item-count="10" :page="paginat.page"
:page-sizes="[10, 20, 30, 40]" :page-size="paginat.limit"
:item-count="paginat.count"
:page-sizes="[12, 24, 36, 48]"
@update:page="changePage"
@update:page-size="changeSize"
show-size-picker show-size-picker
/> />
</div> </div>
</div> </div>
<!-- model -->
<project-items-modal-card <project-items-modal-card
v-if="modalData" v-if="modalData"
v-model:modalShow="modalShow" v-model:modalShow="modalShow"
@ -40,9 +53,8 @@ import { useModalDataInit } from './hooks/useModal.hook'
import { useDataListInit } from './hooks/useData.hook' import { useDataListInit } from './hooks/useData.hook'
const { CopyIcon, EllipsisHorizontalCircleSharpIcon } = icon.ionicons5 const { CopyIcon, EllipsisHorizontalCircleSharpIcon } = icon.ionicons5
const { list, deleteHandle } = useDataListInit() const { modalData, modalShow, closeModal, previewHandle, resizeHandle, editHandle } = useModalDataInit()
const { modalData, modalShow, closeModal, resizeHandle, editHandle } = const { loading, paginat, list, changeSize, changePage, releaseHandle, copyHandle, deleteHandle } = useDataListInit()
useModalDataInit()
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -51,7 +63,7 @@ $contentHeight: 250px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
min-height: calc(100vh - #{$--header-height} * 2 - 2px); min-height: calc(100vh - #{$--header-height} - 40px - 2px);
.list-content { .list-content {
position: relative; position: relative;
height: $contentHeight; height: $contentHeight;

View File

@ -11,7 +11,7 @@
<n-space class="list-content-top go-px-0" justify="center"> <n-space class="list-content-top go-px-0" justify="center">
<n-space> <n-space>
<n-text> <n-text>
{{ cardData?.title || '' }} {{ cardData?.title || cardData?.id || '未命名' }}
</n-text> </n-text>
</n-space> </n-space>
</n-space> </n-space>
@ -26,9 +26,7 @@
<!-- 中间 --> <!-- 中间 -->
<div class="list-content-img"> <div class="list-content-img">
<img <img
:src=" :src="cardData?.image"
requireUrl('project/moke-20211219181327.png')
"
:alt="cardData?.title" :alt="cardData?.title"
/> />
</div> </div>
@ -75,10 +73,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue' import { reactive, PropType } from 'vue'
import { renderIcon, renderLang } from '@/utils' import { renderIcon, renderLang } from '@/utils'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { MacOsControlBtn } from '@/components/Tips/MacOsControlBtn' import { MacOsControlBtn } from '@/components/Tips/MacOsControlBtn'
import { Chartype } from '../../index.d'
const { HammerIcon } = icon.ionicons5 const { HammerIcon } = icon.ionicons5
@ -86,14 +85,9 @@ const emit = defineEmits(['close', 'edit'])
const props = defineProps({ const props = defineProps({
modalShow: Boolean, modalShow: Boolean,
cardData: Object cardData: Object as PropType<Chartype>
}) })
// url
const requireUrl = (name: string) => {
return new URL(`../../../../../assets/images/${name}`, import.meta.url).href
}
const fnBtnList = reactive([ const fnBtnList = reactive([
{ {
label: renderLang('global.r_edit'), label: renderLang('global.r_edit'),
@ -124,12 +118,14 @@ const closeHandle = () => {
<style lang="scss" scoped> <style lang="scss" scoped>
$padding: 30px; $padding: 30px;
$contentHeight: calc(80vh); $contentHeight: calc(80vh);
$imageHeight: calc(80vh - 110px);
$contentWidth: calc(82vw); $contentWidth: calc(82vw);
@include go('modal-box') { @include go('modal-box') {
width: $contentWidth; width: $contentWidth;
height: $contentHeight;
.list-content { .list-content {
margin-top: 28px; margin-top: 20px;
border-radius: $--border-radius-base; border-radius: $--border-radius-base;
overflow: hidden; overflow: hidden;
@include background-image('background-point'); @include background-image('background-point');
@ -144,8 +140,9 @@ $contentWidth: calc(82vw);
} }
&-img { &-img {
@extend .go-flex-center; @extend .go-flex-center;
padding: 6px 0;
img { img {
max-height: $contentHeight; height: $imageHeight;
min-height: 200px; min-height: 200px;
max-width: 100%; max-width: 100%;
@extend .go-border-radius; @extend .go-border-radius;

View File

@ -2,7 +2,10 @@ export type Chartype = {
id: number | string id: number | string
title: string // 标题 title: string // 标题
label: string // 标签 label: string // 标签
release: boolean // 0未发布 | 1已发布 time: string, // 时间
image: string, // 预览图地址
createId: string, // 创建者
release: boolean // false 未发布 | true 已发布
} }
export type ChartList = Chartype[] export type ChartList = Chartype[]

View File

@ -18,7 +18,7 @@
:disabled="item.disabled" :disabled="item.disabled"
v-for="item in typeList" v-for="item in typeList"
:key="item.key" :key="item.key"
@click="btnHandle" @click="btnHandle(item.key)"
> >
<component :is="item.title"></component> <component :is="item.title"></component>
<template #icon> <template #icon>
@ -35,10 +35,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, reactive } from 'vue' import { watch } from 'vue'
import { icon } from '@/plugins' import { icon } from '@/plugins'
import { PageEnum, ChartEnum } from '@/enums/pageEnum' import { PageEnum, ChartEnum } from '@/enums/pageEnum'
import { ResultEnum } from '@/enums/httpEnum'
import { fetchPathByName, routerTurnByPath, renderLang, getUUID } from '@/utils' import { fetchPathByName, routerTurnByPath, renderLang, getUUID } from '@/utils'
import { BackEndFactory } from '@/backend/ibackend'
const { FishIcon, CloseIcon } = icon.ionicons5 const { FishIcon, CloseIcon } = icon.ionicons5
const { StoreIcon, ObjectStorageIcon } = icon.carbon const { StoreIcon, ObjectStorageIcon } = icon.carbon
@ -48,7 +50,7 @@ const props = defineProps({
show: Boolean show: Boolean
}) })
const typeList = reactive([ const typeList = [
{ {
title: renderLang('project.new_project'), title: renderLang('project.new_project'),
key: ChartEnum.CHART_HOME_NAME, key: ChartEnum.CHART_HOME_NAME,
@ -67,7 +69,7 @@ const typeList = reactive([
icon: StoreIcon, icon: StoreIcon,
disabled: true disabled: true
} }
]) ]
// @on-after-leave // @on-after-leave
watch(props, newValue => { watch(props, newValue => {
@ -82,11 +84,32 @@ const closeHandle = () => {
} }
// //
const btnHandle = (key: string) => { const btnHandle = async (key: string) => {
closeHandle() switch (key) {
const id = getUUID() case ChartEnum.CHART_HOME_NAME:
try {
//
const res = await BackEndFactory.createProject({
//
projectName: getUUID(),
// remarks
remarks: null,
//
indexImage: null,
}) as any
if(res.code === ResultEnum.SUCCESS) {
window['$message'].success(window['$t']('project.create_success'))
const { id } = res.data
const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href') const path = fetchPathByName(ChartEnum.CHART_HOME_NAME, 'href')
routerTurnByPath(path, [id], undefined, true) routerTurnByPath(path, [id], undefined, true)
closeHandle()
}
} catch (error) {
window['$message'].error(window['$t']('project.create_failure'))
}
break;
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,35 @@
<template>
<div class="go-redirect-un-publish">
<div class="text-center">
<img src="~@/assets/images/exception/nodata.svg" alt="" />
</div>
<div class="text-center">
<h1>当前项目暂未发布</h1>
</div>
</div>
</template>
<style lang="scss" scoped>
@include go(redirect-un-publish) {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
height: 100vh;
overflow: hidden;
padding: 100px 0;
@include background-image('background-image');
.text-center {
h1 {
color: #666;
padding: 20px 0;
}
}
img {
width: 350px;
margin: 0 auto;
}
}
</style>