This commit is contained in:
ahjung
2024-04-09 16:28:28 +08:00
parent 260397f3ae
commit ee8aed2f62
23 changed files with 2652 additions and 2346 deletions

View File

@@ -1,5 +1,11 @@
# CHANGELOG # CHANGELOG
## 1.9.1
- 优化 `typeSctipt` 类型定义
- 优化 `setup` 语法
- 依赖升级
## 1.9.0 ## 1.9.0
- 新增 `BasicForm` 组件,支持 `setLoading`, `setSchema` 方法 - 新增 `BasicForm` 组件,支持 `setLoading`, `setSchema` 方法

View File

@@ -1,6 +1,6 @@
{ {
"name": "naive-ui-admin", "name": "naive-ui-admin",
"version": "1.9.0", "version": "1.9.1",
"author": { "author": {
"name": "Ahjung", "name": "Ahjung",
"email": "735878602@qq.com", "email": "735878602@qq.com",
@@ -32,71 +32,71 @@
"@vicons/ionicons5": "^0.12.0", "@vicons/ionicons5": "^0.12.0",
"@vueup/vue-quill": "^1.2.0", "@vueup/vue-quill": "^1.2.0",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^9.13.0",
"axios": "^1.4.0", "axios": "^1.6.8",
"blueimp-md5": "^2.19.0", "blueimp-md5": "^2.19.0",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"echarts": "^5.4.3", "echarts": "^5.5.0",
"element-resize-detector": "^1.2.4", "element-resize-detector": "^1.2.4",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"naive-ui": "^2.34.4", "naive-ui": "^2.38.1",
"pinia": "^2.1.6", "pinia": "^2.1.7",
"qs": "^6.11.2", "qs": "^6.12.0",
"vfonts": "^0.0.3", "vfonts": "^0.0.3",
"vue": "^3.3.4", "vue": "^3.4.21",
"vue-router": "^4.2.4", "vue-router": "^4.3.0",
"vue-types": "^4.2.1" "vue-types": "^4.2.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.7.0", "@commitlint/cli": "^17.8.1",
"@commitlint/config-conventional": "^17.7.0", "@commitlint/config-conventional": "^17.8.1",
"@types/lodash": "^4.14.197", "@types/lodash": "^4.17.0",
"@types/node": "^18.17.4", "@types/node": "^18.19.31",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-vue": "^3.2.0", "@vitejs/plugin-vue": "^3.2.0",
"@vitejs/plugin-vue-jsx": "^2.1.1", "@vitejs/plugin-vue-jsx": "^2.1.1",
"@vue/compiler-sfc": "^3.3.4", "@vue/compiler-sfc": "^3.4.21",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^11.0.3",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.19",
"commitizen": "^4.3.0", "commitizen": "^4.3.0",
"core-js": "^3.32.0", "core-js": "^3.36.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "^16.3.1", "dotenv": "^16.4.5",
"eslint": "^8.46.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^8.10.0", "eslint-config-prettier": "^8.10.0",
"eslint-define-config": "1.12.0", "eslint-define-config": "1.12.0",
"eslint-plugin-jest": "^27.2.3", "eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.17.0", "eslint-plugin-vue": "^9.24.1",
"esno": "^0.16.3", "esno": "^0.16.3",
"gh-pages": "^4.0.0", "gh-pages": "^4.0.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"jest": "^29.6.2", "jest": "^29.7.0",
"less": "^4.2.0", "less": "^4.2.0",
"less-loader": "^11.1.3", "less-loader": "^11.1.4",
"lint-staged": "^13.2.3", "lint-staged": "^13.3.0",
"postcss": "^8.4.27", "postcss": "^8.4.38",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"pretty-quick": "^3.1.3", "pretty-quick": "^3.3.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"stylelint": "^14.16.1", "stylelint": "^14.16.1",
"stylelint-config-prettier": "^9.0.5", "stylelint-config-prettier": "^9.0.5",
"stylelint-config-standard": "^29.0.0", "stylelint-config-standard": "^29.0.0",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"stylelint-scss": "^4.7.0", "stylelint-scss": "^4.7.0",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.4.3",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"unplugin-vue-components": "^0.22.12", "unplugin-vue-components": "^0.22.12",
"vite": "^3.2.7", "vite": "^3.2.10",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0", "vite-plugin-html": "^3.2.2",
"vite-plugin-mock": "^2.9.8", "vite-plugin-mock": "^2.9.8",
"vite-plugin-style-import": "^2.0.0", "vite-plugin-style-import": "^2.0.0",
"vue-demi": "^0.13.11", "vue-demi": "^0.13.11",
"vue-draggable-next": "^2.2.1", "vue-draggable-next": "^2.2.1",
"vue-eslint-parser": "^9.3.1", "vue-eslint-parser": "^9.4.2",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"lint-staged": { "lint-staged": {

3613
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -81,7 +81,3 @@
document.removeEventListener('mousedown', timekeeping); document.removeEventListener('mousedown', timekeeping);
}); });
</script> </script>
<style lang="less">
@import 'styles/index.less';
</style>

View File

@@ -8,7 +8,7 @@
:style="getCSSProperties" :style="getCSSProperties"
v-for="(item, index) in imgList" v-for="(item, index) in imgList"
:key="`img_${index}`" :key="`img_${index}`"
> >
<div class="upload-card-item-info"> <div class="upload-card-item-info">
<div class="img-box"> <div class="img-box">
<img :src="item" /> <img :src="item" />
@@ -69,243 +69,243 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, toRefs, reactive, computed, watch } from 'vue'; import { defineComponent, toRefs, reactive, computed, watch } from 'vue';
import { EyeOutlined, DeleteOutlined, PlusOutlined } from '@vicons/antd'; import { EyeOutlined, DeleteOutlined, PlusOutlined } from '@vicons/antd';
import { basicProps } from './props'; import { basicProps } from './props';
import { useMessage, useDialog } from 'naive-ui'; import { useMessage, useDialog } from 'naive-ui';
import { ResultEnum } from '@/enums/httpEnum'; import { ResultEnum } from '@/enums/httpEnum';
import componentSetting from '@/settings/componentSetting'; import componentSetting from '@/settings/componentSetting';
import { useGlobSetting } from '@/hooks/setting'; import { useGlobSetting } from '@/hooks/setting';
import { isString } from '@/utils/is'; import { isString } from '@/utils/is';
const globSetting = useGlobSetting(); const globSetting = useGlobSetting();
export default defineComponent({ export default defineComponent({
name: 'BasicUpload', name: 'BasicUpload',
components: { EyeOutlined, DeleteOutlined, PlusOutlined }, components: { EyeOutlined, DeleteOutlined, PlusOutlined },
props: { props: {
...basicProps, ...basicProps,
}, },
emits: ['uploadChange', 'delete'], emits: ['uploadChange', 'delete'],
setup(props, { emit }) { setup(props, { emit }) {
const getCSSProperties = computed(() => { const getCSSProperties = computed(() => {
return { return {
width: `${props.width}px`, width: `${props.width}px`,
height: `${props.height}px`, height: `${props.height}px`,
}; };
});
const message = useMessage();
const dialog = useDialog();
const state = reactive({
showModal: false,
previewUrl: '',
originalImgList: [] as string[],
imgList: [] as string[],
});
//赋值默认图片显示
watch(
() => props.value,
() => {
state.imgList = props.value.map((item) => {
return getImgUrl(item);
});
},
{ immediate: true }
);
//预览
function preview(url: string) {
state.showModal = true;
state.previewUrl = url;
}
//删除
function remove(index: number) {
dialog.info({
title: '提示',
content: '你确定要删除吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
state.imgList.splice(index, 1);
state.originalImgList.splice(index, 1);
emit('uploadChange', state.originalImgList);
emit('delete', state.originalImgList);
},
onNegativeClick: () => {},
}); });
}
//组装完整图片地址 const message = useMessage();
function getImgUrl(url: string): string { const dialog = useDialog();
const { imgUrl } = globSetting;
return /(^http|https:\/\/)/g.test(url) ? url : `${imgUrl}${url}`;
}
function checkFileType(fileType: string) { const state = reactive({
return componentSetting.upload.fileType.includes(fileType); showModal: false,
} previewUrl: '',
originalImgList: [] as string[],
imgList: [] as string[],
});
//上传之前 //赋值默认图片显示
function beforeUpload({ file }) { watch(
const fileInfo = file.file; () => props.value,
const { maxSize, accept } = props; () => {
const acceptRef = (isString(accept) && accept.split(',')) || []; state.imgList = props.value.map((item) => {
return getImgUrl(item);
});
},
{ immediate: true }
);
// 设置最大值,则判断 //预览
if (maxSize && fileInfo.size / 1024 / 1024 >= maxSize) { function preview(url: string) {
message.error(`上传文件最大值不能超过${maxSize}M`); state.showModal = true;
return false; state.previewUrl = url;
} }
// 设置类型,则判断 //删除
const fileType = componentSetting.upload.fileType; function remove(index: number) {
if (acceptRef.length > 0 && !checkFileType(fileInfo.type)) { dialog.info({
message.error(`只能上传文件类型为${fileType.join(',')}`); title: '提示',
return false; content: '你确定要删除吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
state.imgList.splice(index, 1);
state.originalImgList.splice(index, 1);
emit('uploadChange', state.originalImgList);
emit('delete', state.originalImgList);
},
onNegativeClick: () => {},
});
} }
return true; //组装完整图片地址
} function getImgUrl(url: string): string {
const { imgUrl } = globSetting;
return /(^http|https:\/\/)/g.test(url) ? url : `${imgUrl}${url}`;
}
//上传结束 function checkFileType(fileType: string) {
function finish({ event: Event }) { return componentSetting.upload.fileType.includes(fileType);
const res = eval('(' + Event.target.response + ')'); }
const infoField = componentSetting.upload.apiSetting.infoField;
const { code } = res;
const message = res.msg || res.message || '上传失败';
const result = res[infoField];
//成功
if (code === ResultEnum.SUCCESS) {
let imgUrl: string = getImgUrl(result.photo);
state.imgList.push(imgUrl);
state.originalImgList.push(result.photo);
emit('uploadChange', state.originalImgList);
} else message.error(message);
}
return { //上传之前
...toRefs(state), function beforeUpload({ file }) {
finish, const fileInfo = file.file;
preview, const { maxSize, accept } = props;
remove, const acceptRef = (isString(accept) && accept.split(',')) || [];
beforeUpload,
getCSSProperties, // 设置最大值,则判断
}; if (maxSize && fileInfo.size / 1024 / 1024 >= maxSize) {
}, message.error(`上传文件最大值不能超过${maxSize}M`);
}); return false;
}
// 设置类型,则判断
const fileType = componentSetting.upload.fileType;
if (acceptRef.length > 0 && !checkFileType(fileInfo.type)) {
message.error(`只能上传文件类型为${fileType.join(',')}`);
return false;
}
return true;
}
//上传结束
function finish({ event: Event }) {
const res = eval('(' + Event.target.response + ')');
const infoField = componentSetting.upload.apiSetting.infoField;
const { code } = res;
const message = res.msg || res.message || '上传失败';
const result = res[infoField];
//成功
if (code === ResultEnum.SUCCESS) {
let imgUrl: string = getImgUrl(result.photo);
state.imgList.push(imgUrl);
state.originalImgList.push(result.photo);
emit('uploadChange', state.originalImgList);
} else message.error(message);
}
return {
...toRefs(state),
finish,
preview,
remove,
beforeUpload,
getCSSProperties,
};
},
});
</script> </script>
<style lang="less"> <style lang="less">
.upload { .upload {
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
&-card { &-card {
width: auto; width: auto;
height: auto; height: auto;
display: flex;
flex-wrap: wrap;
align-items: center;
&-item {
margin: 0 8px 8px 0;
position: relative;
padding: 8px;
border: 1px solid #d9d9d9;
border-radius: 2px;
display: flex; display: flex;
justify-content: center; flex-wrap: wrap;
flex-direction: column;
align-items: center; align-items: center;
&:hover { &-item {
background: 0 0; margin: 0 8px 8px 0;
.upload-card-item-info::before {
opacity: 1;
}
&-info::before {
opacity: 1;
}
}
&-info {
position: relative; position: relative;
height: 100%; padding: 8px;
width: 100%; border: 1px solid #d9d9d9;
padding: 0; border-radius: 2px;
overflow: hidden; display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
&:hover { &:hover {
.img-box-actions { background: 0 0;
.upload-card-item-info::before {
opacity: 1;
}
&-info::before {
opacity: 1; opacity: 1;
} }
} }
&::before { &-info {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: all 0.3s;
content: ' ';
}
.img-box {
position: relative; position: relative;
//padding: 8px; height: 100%;
//border: 1px solid #d9d9d9; width: 100%;
border-radius: 2px; padding: 0;
} overflow: hidden;
.img-box-actions {
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
white-space: nowrap;
transform: translate(-50%, -50%);
opacity: 0;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: space-between;
&:hover { &:hover {
background: 0 0; .img-box-actions {
opacity: 1;
}
} }
.action-icon { &::before {
color: rgba(255, 255, 255, 0.85); position: absolute;
z-index: 1;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: all 0.3s;
content: ' ';
}
.img-box {
position: relative;
//padding: 8px;
//border: 1px solid #d9d9d9;
border-radius: 2px;
}
.img-box-actions {
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
white-space: nowrap;
transform: translate(-50%, -50%);
opacity: 0;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: space-between;
&:hover { &:hover {
cursor: pointer; background: 0 0;
color: #fff; }
.action-icon {
color: rgba(255, 255, 255, 0.85);
&:hover {
cursor: pointer;
color: #fff;
}
} }
} }
} }
} }
}
&-item-select-picture { &-item-select-picture {
border: 1px dashed #d9d9d9; border: 1px dashed #d9d9d9;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
background: #fafafa; background: #fafafa;
color: #666;
.upload-title {
color: #666; color: #666;
.upload-title {
color: #666;
}
} }
} }
} }
}
</style> </style>

View File

@@ -10,7 +10,7 @@
<h2 v-show="!collapsed" class="title">{{ websiteConfig.title }}</h2> <h2 v-show="!collapsed" class="title">{{ websiteConfig.title }}</h2>
</div> </div>
<AsideMenu <AsideMenu
v-model:collapsed="collapsed" :collapsed="collapsed"
v-model:location="getMenuLocation" v-model:location="getMenuLocation"
:inverted="getInverted" :inverted="getInverted"
mode="horizontal" mode="horizontal"
@@ -21,7 +21,7 @@
<!-- 菜单收起 --> <!-- 菜单收起 -->
<div <div
class="ml-1 layout-header-trigger layout-header-trigger-min" class="ml-1 layout-header-trigger layout-header-trigger-min"
@click="() => $emit('update:collapsed', !collapsed)" @click="handleMenuCollapsed"
> >
<n-icon size="18" v-if="collapsed"> <n-icon size="18" v-if="collapsed">
<MenuUnfoldOutlined /> <MenuUnfoldOutlined />
@@ -101,12 +101,14 @@
<div class="layout-header-trigger layout-header-trigger-min"> <div class="layout-header-trigger layout-header-trigger-min">
<n-dropdown trigger="hover" @select="avatarSelect" :options="avatarOptions"> <n-dropdown trigger="hover" @select="avatarSelect" :options="avatarOptions">
<div class="avatar"> <div class="avatar">
<n-avatar round> <n-avatar round :src="websiteConfig.logo">
{{ username }}
<template #icon> <template #icon>
<UserOutlined /> <UserOutlined />
</template> </template>
</n-avatar> </n-avatar>
<n-divider vertical />
<span>{{ username }}</span>
</div> </div>
</n-dropdown> </n-dropdown>
</div> </div>
@@ -151,19 +153,18 @@
type: Boolean, type: Boolean,
}, },
}, },
setup(props) { emits: ['update:collapsed'],
setup(props, { emit }) {
const userStore = useUserStore(); const userStore = useUserStore();
const useLockscreen = useScreenLockStore(); const useLockscreen = useScreenLockStore();
const message = useMessage(); const message = useMessage();
const dialog = useDialog(); const dialog = useDialog();
const { navMode, navTheme, headerSetting, menuSetting, crumbsSetting } = useProjectSetting(); const { navMode, navTheme, headerSetting, menuSetting, crumbsSetting } = useProjectSetting();
const { name } = userStore?.info || {};
const drawerSetting = ref(); const drawerSetting = ref();
const state = reactive({ const state = reactive({
username: name ?? '', username: userStore?.info?.username ?? '',
fullscreenIcon: 'FullscreenOutlined', fullscreenIcon: 'FullscreenOutlined',
navMode, navMode,
navTheme, navTheme,
@@ -323,6 +324,10 @@
openDrawer(); openDrawer();
} }
function handleMenuCollapsed() {
emit('update:collapsed', !props.collapsed);
}
return { return {
...toRefs(state), ...toRefs(state),
iconList, iconList,
@@ -341,6 +346,7 @@
getMenuLocation, getMenuLocation,
mixMenu, mixMenu,
websiteConfig, websiteConfig,
handleMenuCollapsed,
}; };
}, },
}); });

View File

@@ -1,4 +1,5 @@
import './styles/tailwind.css'; import './styles/tailwind.css';
import './styles/index.less';
import { createApp } from 'vue'; import { createApp } from 'vue';
import { setupNaiveDiscreteApi, setupNaive, setupDirectives } from '@/plugins'; import { setupNaiveDiscreteApi, setupNaive, setupDirectives } from '@/plugins';
import App from './App.vue'; import App from './App.vue';

View File

@@ -8,7 +8,7 @@ import { storage } from '@/utils/Storage';
export type UserInfoType = { export type UserInfoType = {
// TODO: add your own data // TODO: add your own data
name: string; username: string;
email: string; email: string;
}; };

View File

@@ -1 +1 @@
@import 'transition/index.less'; @import url('./transition/index.less');

View File

@@ -54,8 +54,8 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, ref, reactive, toRefs } from 'vue'; import { ref, reactive } from 'vue';
import { useMessage } from 'naive-ui'; import { useMessage } from 'naive-ui';
import { basicModal, useModal } from '@/components/Modal'; import { basicModal, useModal } from '@/components/Modal';
import { BasicForm, FormSchema, useForm } from '@/components/Form/index'; import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
@@ -205,90 +205,62 @@
}, },
]; ];
export default defineComponent({ const modalRef: any = ref(null);
components: { basicModal, BasicForm }, const message = useMessage();
setup() {
const modalRef: any = ref(null);
const message = useMessage();
const [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({ const [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({
title: '新增预约', title: '新增预约',
});
const [
lightModalRegister,
{
openModal: lightOpenModal,
closeModal: lightCloseModal,
setSubLoading: lightSetSubLoading,
},
] = useModal({
title: '确认对话框',
showIcon: true,
type: 'warning',
closable: false,
maskClosable: true,
});
const [register, { submit }] = useForm({
gridProps: { cols: 1 },
collapsedRows: 3,
labelWidth: 120,
layout: 'horizontal',
submitButtonText: '提交预约',
showActionButtonGroup: false,
schemas,
});
const state = reactive({
formValue: {
name: '小马哥',
},
});
async function okModal() {
const formRes = await submit();
if (formRes) {
closeModal();
console.log('formRes', formRes);
message.success('提交成功');
} else {
message.error('验证失败,请填写完整信息');
setSubLoading(false);
}
}
function lightOkModal() {
lightCloseModal();
lightSetSubLoading(false);
}
function showLightModal() {
lightOpenModal();
}
function showModal() {
openModal();
}
function handleReset(values: Recordable) {
console.log(values);
}
return {
...toRefs(state),
modalRef,
register,
modalRegister,
lightModalRegister,
handleReset,
showModal,
okModal,
lightOkModal,
showLightModal,
};
},
}); });
const [
lightModalRegister,
{ openModal: lightOpenModal, closeModal: lightCloseModal, setSubLoading: lightSetSubLoading },
] = useModal({
title: '确认对话框',
showIcon: true,
type: 'warning',
closable: false,
maskClosable: true,
});
const [register, { submit }] = useForm({
gridProps: { cols: 1 },
collapsedRows: 3,
labelWidth: 120,
layout: 'horizontal',
submitButtonText: '提交预约',
showActionButtonGroup: false,
schemas,
});
async function okModal() {
const formRes = await submit();
if (formRes) {
closeModal();
console.log('formRes', formRes);
message.success('提交成功');
} else {
message.error('验证失败,请填写完整信息');
setSubLoading(false);
}
}
function lightOkModal() {
lightCloseModal();
lightSetSubLoading(false);
}
function showLightModal() {
lightOpenModal();
}
function showModal() {
openModal();
}
function handleReset(values: Recordable) {
console.log(values);
}
</script> </script>
<style lang="less"> <style lang="less">

View File

@@ -51,7 +51,7 @@
actionRef.value.reload(); actionRef.value.reload();
} }
function editEnd({ record, index, key, value }) { function editEnd({ value }) {
console.log(value); console.log(value);
} }
</script> </script>

View File

@@ -106,7 +106,7 @@
actionRef.value.reload(); actionRef.value.reload();
} }
function editEnd({ record, index, key, value }) { function editEnd({ value }) {
console.log(value); console.log(value);
} }
</script> </script>

View File

@@ -1,111 +1,108 @@
<template> <template>
<div ref="chartRef" :style="{ height, width }"></div> <div ref="chartRef" :style="{ height, width }"></div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, onMounted, ref, Ref } from 'vue'; import { onMounted, ref, Ref } from 'vue';
import { useECharts } from '@/hooks/web/useECharts'; import { useECharts } from '@/hooks/web/useECharts';
import { basicProps } from './props'; import { basicProps } from './props';
export default defineComponent({ defineProps({
props: basicProps, ...basicProps,
setup() { });
const chartRef = ref<HTMLDivElement | null>(null); const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
onMounted(() => { onMounted(() => {
setOptions({ setOptions({
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
lineStyle: { lineStyle: {
width: 1, width: 1,
color: '#019680', color: '#019680',
}, },
},
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [
'6:00',
'7:00',
'8:00',
'9:00',
'10:00',
'11:00',
'12:00',
'13:00',
'14:00',
'15:00',
'16:00',
'17:00',
'18:00',
'19:00',
'20:00',
'21:00',
'22:00',
'23:00',
],
splitLine: {
show: true,
lineStyle: {
width: 1,
type: 'solid',
color: 'rgba(226,226,226,0.5)',
},
},
axisTick: {
show: false,
},
},
yAxis: [
{
type: 'value',
max: 80000,
splitNumber: 4,
axisTick: {
show: false,
},
splitArea: {
show: true,
areaStyle: {
color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
}, },
}, },
xAxis: { },
type: 'category', ],
boundaryGap: false, grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
data: [ series: [
'6:00', {
'7:00', smooth: true,
'8:00', data: [
'9:00', 111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, 22222,
'10:00', 11111, 4000, 2000, 500, 333, 222, 111,
'11:00', ],
'12:00', type: 'line',
'13:00', areaStyle: {},
'14:00', itemStyle: {
'15:00', color: '#5ab1ef',
'16:00',
'17:00',
'18:00',
'19:00',
'20:00',
'21:00',
'22:00',
'23:00',
],
splitLine: {
show: true,
lineStyle: {
width: 1,
type: 'solid',
color: 'rgba(226,226,226,0.5)',
},
},
axisTick: {
show: false,
},
}, },
yAxis: [ },
{ {
type: 'value', smooth: true,
max: 80000, data: [
splitNumber: 4, 33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201, 390,
axisTick: { 198, 60, 30, 22, 11,
show: false,
},
splitArea: {
show: true,
areaStyle: {
color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
},
},
},
], ],
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true }, type: 'line',
series: [ areaStyle: {},
{ itemStyle: {
smooth: true, color: '#019680',
data: [ },
111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, },
22222, 11111, 4000, 2000, 500, 333, 222, 111, ],
], });
type: 'line',
areaStyle: {},
itemStyle: {
color: '#5ab1ef',
},
},
{
smooth: true,
data: [
33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201,
390, 198, 60, 30, 22, 11,
],
type: 'line',
areaStyle: {},
itemStyle: {
color: '#019680',
},
},
],
});
});
return { chartRef };
},
}); });
</script> </script>

View File

@@ -16,15 +16,7 @@
</NRow> </NRow>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import FluxTrend from './FluxTrend.vue'; import FluxTrend from './FluxTrend.vue';
import VisitAmount from './VisitAmount.vue'; import VisitAmount from './VisitAmount.vue';
export default defineComponent({
components: { FluxTrend, VisitAmount },
setup() {
return {};
},
});
</script> </script>

View File

@@ -1,61 +1,59 @@
<template> <template>
<div ref="chartRef" :style="{ height, width }"></div> <div ref="chartRef" :style="{ height, width }"></div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, onMounted, ref, Ref } from 'vue'; import { onMounted, ref, Ref } from 'vue';
import { useECharts } from '@/hooks/web/useECharts'; import { useECharts } from '@/hooks/web/useECharts';
import { basicProps } from './props'; import { basicProps } from './props';
export default defineComponent({ defineProps({
props: basicProps, ...basicProps,
setup() { });
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
onMounted(() => { const chartRef = ref<HTMLDivElement | null>(null);
setOptions({ const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
tooltip: {
trigger: 'axis', onMounted(() => {
axisPointer: { setOptions({
lineStyle: { tooltip: {
width: 1, trigger: 'axis',
color: '#019680', axisPointer: {
}, lineStyle: {
}, width: 1,
color: '#019680',
}, },
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true }, },
xAxis: { },
type: 'category', grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
data: [ xAxis: {
'1月', type: 'category',
'2月', data: [
'3月', '1月',
'4月', '2月',
'5月', '3月',
'6月', '4月',
'7月', '5月',
'8月', '6月',
'9月', '7月',
'10月', '8月',
'11月', '9月',
'12月', '10月',
], '11月',
}, '12月',
yAxis: { ],
type: 'value', },
max: 8000, yAxis: {
splitNumber: 4, type: 'value',
}, max: 8000,
series: [ splitNumber: 4,
{ },
data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800], series: [
type: 'bar', {
barMaxWidth: 80, data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
}, type: 'bar',
], barMaxWidth: 80,
}); },
}); ],
return { chartRef }; });
},
}); });
</script> </script>

View File

@@ -241,11 +241,38 @@
SettingOutlined, SettingOutlined,
} from '@vicons/antd'; } from '@vicons/antd';
interface InVisits {
dayVisits: number;
rise: number;
decline: number;
amount: number;
}
interface InSaleroom {
weekSaleroom: number;
amount: number;
degree: number;
}
interface InOrderLarge {
weekLarge: number;
rise: number;
decline: number;
amount: number;
}
interface InVolume {
weekLarge: number;
rise: number;
decline: number;
amount: number;
}
const loading = ref(true); const loading = ref(true);
const visits = ref<any>({}); const visits = ref({} as InVisits);
const saleroom = ref<any>({}); const saleroom = ref({} as InSaleroom);
const orderLarge = ref<any>({}); const orderLarge = ref({} as InOrderLarge);
const volume = ref(<any>{}); const volume = ref({} as InVolume);
// 图标列表 // 图标列表
const iconList = [ const iconList = [

View File

@@ -13,7 +13,7 @@
<n-step title="完成转账" description="恭喜您,转账成功" /> <n-step title="完成转账" description="恭喜您,转账成功" />
</n-steps> </n-steps>
<step1 v-if="currentTab === 1" @next-step="nextStep" /> <step1 v-if="currentTab === 1" @next-step="nextStep" />
<step2 v-if="currentTab === 2" @next-step="nextStep" @prevStep="prevStep" /> <step2 v-if="currentTab === 2" @next-step="nextStep" @prev-step="prevStep" />
<step3 v-if="currentTab === 3" @prev-step="prevStep" @finish="finish" /> <step3 v-if="currentTab === 3" @prev-step="prevStep" @finish="finish" />
</n-space> </n-space>
</n-card> </n-card>
@@ -21,7 +21,7 @@
</template> </template>
<script setup> <script setup>
import { defineComponent, ref } from 'vue'; import { ref } from 'vue';
import step1 from './Step1.vue'; import step1 from './Step1.vue';
import step2 from './Step2.vue'; import step2 from './Step2.vue';
import step3 from './Step3.vue'; import step3 from './Step3.vue';

View File

@@ -3,7 +3,7 @@
<div class="n-layout-page-header"> <div class="n-layout-page-header">
<n-card :bordered="false" title="基础详情"> 基础详情有时也用于显示只读信息 </n-card> <n-card :bordered="false" title="基础详情"> 基础详情有时也用于显示只读信息 </n-card>
</div> </div>
<n-card :bordered="false" class="proCard mt-4" size="small" :segmented="{ content: true }"> <n-card :bordered="false" class="mt-4 proCard" size="small" :segmented="{ content: true }">
<n-descriptions label-placement="left" class="py-2"> <n-descriptions label-placement="left" class="py-2">
<n-descriptions-item> <n-descriptions-item>
<template #label>收款人姓名</template> <template #label>收款人姓名</template>
@@ -21,14 +21,6 @@
</div> </div>
</template> </template>
<script> <script lang="ts" setup></script>
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
return {};
},
});
</script>
<style lang="less" scoped></style> <style lang="less" scoped></style>

View File

@@ -53,8 +53,8 @@
</n-grid> </n-grid>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';
import { useDialog, useMessage } from 'naive-ui'; import { useDialog, useMessage } from 'naive-ui';
const rules = { const rules = {
@@ -70,64 +70,49 @@
}, },
}; };
export default defineComponent({ const formRef: any = ref(null);
setup() { const message = useMessage();
const formRef: any = ref(null); const dialog = useDialog();
const message = useMessage();
const dialog = useDialog();
const state = reactive({ const formValue = ref({
formValue: { name: '',
name: '', mobile: '',
mobile: '', icpCode: '',
icpCode: '', address: '',
address: '', loginCode: 0,
loginCode: 0, closeText:
closeText: '网站维护中,暂时无法访问!本网站正在进行系统维护和技术升级,网站暂时无法访问,敬请谅解!',
'网站维护中,暂时无法访问!本网站正在进行系统维护和技术升级,网站暂时无法访问,敬请谅解!', systemOpen: true,
systemOpen: true, });
function systemOpenChange(value) {
if (!value) {
dialog.warning({
title: '提示',
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
message.success('操作成功');
},
onNegativeClick: () => {
formValue.value.systemOpen = true;
}, },
}); });
}
}
function systemOpenChange(value) { function formSubmit() {
if (!value) { formRef.value.validate((errors) => {
dialog.warning({ if (!errors) {
title: '提示', message.success('验证成功');
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!', } else {
positiveText: '确定', message.error('验证失败,请填写完整信息');
negativeText: '取消',
onPositiveClick: () => {
message.success('操作成功');
},
onNegativeClick: () => {
state.formValue.systemOpen = true;
},
});
}
} }
});
}
function formSubmit() { function resetForm() {
formRef.value.validate((errors) => { formRef.value.restoreValidation();
if (!errors) { }
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
function resetForm() {
formRef.value.restoreValidation();
}
return {
formRef,
...toRefs(state),
rules,
formSubmit,
resetForm,
systemOpenChange,
};
},
});
</script> </script>

View File

@@ -36,8 +36,8 @@
</n-grid> </n-grid>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';
import { useMessage } from 'naive-ui'; import { useMessage } from 'naive-ui';
const rules = { const rules = {
@@ -47,33 +47,21 @@
trigger: 'blur', trigger: 'blur',
}, },
}; };
export default defineComponent({
setup() {
const formRef: any = ref(null);
const message = useMessage();
const state = reactive({ const formRef: any = ref(null);
formValue: { const message = useMessage();
originator: '',
},
});
function formSubmit() { const formValue = ref({
formRef.value.validate((errors) => { originator: '',
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
return {
formRef,
...toRefs(state),
rules,
formSubmit,
};
},
}); });
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
</script> </script>

View File

@@ -88,8 +88,8 @@
</n-grid> </n-grid>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';
import { useDialog, useMessage } from 'naive-ui'; import { useDialog, useMessage } from 'naive-ui';
const rules = { const rules = {
@@ -152,67 +152,49 @@
}, },
]; ];
export default defineComponent({ const formRef: any = ref(null);
setup() { const message = useMessage();
const formRef: any = ref(null); const dialog = useDialog();
const message = useMessage();
const dialog = useDialog();
const state = reactive({ const formValue = ref({
formValue: { bigWidth: '',
bigWidth: '', bigHeight: '',
bigHeight: '', smallWidth: '',
smallWidth: '', smallHeight: '',
smallHeight: '', watermarkClarity: null,
watermarkClarity: null, pricePrecise: 1,
pricePrecise: 1, isMarketPrice: true,
isMarketPrice: true, pricePreciseNum: null,
pricePreciseNum: null, });
function systemOpenChange(value) {
if (!value) {
dialog.warning({
title: '提示',
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
message.success('操作成功');
},
onNegativeClick: () => {
formValue.value.systemOpen = true;
}, },
}); });
}
}
function systemOpenChange(value) { function formSubmit() {
if (!value) { formRef.value.validate((errors) => {
dialog.warning({ if (!errors) {
title: '提示', message.success('验证成功');
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!', } else {
positiveText: '确定', message.error('验证失败,请填写完整信息');
negativeText: '取消',
onPositiveClick: () => {
message.success('操作成功');
},
onNegativeClick: () => {
state.formValue.systemOpen = true;
},
});
}
} }
});
}
function formSubmit() { function resetForm() {
formRef.value.validate((errors) => { formRef.value.restoreValidation();
if (!errors) { }
message.success('验证成功');
} else {
message.error('验证失败,请填写完整信息');
}
});
}
function resetForm() {
formRef.value.restoreValidation();
}
return {
formRef,
...toRefs(state),
pricePreciseList,
watermarkPlaceList,
pricePreciseNumList,
rules,
formSubmit,
resetForm,
systemOpenChange,
};
},
});
</script> </script>

View File

@@ -25,8 +25,8 @@
</n-grid> </n-grid>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, toRefs } from 'vue'; import { reactive, toRefs } from 'vue';
import BasicSetting from './BasicSetting.vue'; import BasicSetting from './BasicSetting.vue';
import RevealSetting from './RevealSetting.vue'; import RevealSetting from './RevealSetting.vue';
import EmailSetting from './EmailSetting.vue'; import EmailSetting from './EmailSetting.vue';
@@ -48,26 +48,16 @@
key: 3, key: 3,
}, },
]; ];
export default defineComponent({
components: { BasicSetting, RevealSetting, EmailSetting },
setup() {
const state = reactive({
type: 1,
typeTitle: '基本设置',
});
function switchType(e) { const state = reactive({
state.type = e.key; type: 1,
state.typeTitle = e.name; typeTitle: '基本设置',
}
return {
...toRefs(state),
switchType,
typeTabList,
};
},
}); });
function switchType(e) {
state.type = e.key;
state.typeTitle = e.name;
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.thing-cell { .thing-cell {

View File

@@ -1,5 +1,5 @@
<template> <template>
<n-drawer v-model:show="isDrawer" :width="width" :placement="placement"> <n-drawer v-model:show="state.isDrawer" :width="width" :placement="state.placement">
<n-drawer-content :title="title" closable> <n-drawer-content :title="title" closable>
<n-form <n-form
:model="formParams" :model="formParams"
@@ -38,7 +38,7 @@
<template #footer> <template #footer>
<n-space> <n-space>
<n-button type="primary" :loading="subLoading" @click="formSubmit">提交</n-button> <n-button type="primary" :loading="state.subLoading" @click="formSubmit">提交</n-button>
<n-button @click="handleReset">重置</n-button> <n-button @click="handleReset">重置</n-button>
</n-space> </n-space>
</template> </template>
@@ -46,8 +46,8 @@
</n-drawer> </n-drawer>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, reactive, ref, toRefs } from 'vue'; import { reactive, ref, toRefs } from 'vue';
import { useMessage } from 'naive-ui'; import { useMessage } from 'naive-ui';
const rules = { const rules = {
@@ -62,75 +62,58 @@
trigger: 'blur', trigger: 'blur',
}, },
}; };
export default defineComponent({
name: 'CreateDrawer', defineProps({
components: {}, title: {
props: { type: String,
title: { default: '添加顶级菜单',
type: String,
default: '添加顶级菜单',
},
width: {
type: Number,
default: 450,
},
}, },
setup() { width: {
const message = useMessage(); type: Number,
const formRef: any = ref(null); default: 450,
const defaultValueRef = () => ({
label: '',
type: 1,
subtitle: '',
openType: 1,
auth: '',
path: '',
hidden: false,
});
const state = reactive({
isDrawer: false,
subLoading: false,
formParams: defaultValueRef(),
placement: 'right' as const,
alertText:
'该功能主要实时预览各种布局效果,更多完整配置在 projectSetting.ts 中设置,建议在生产环境关闭该布局预览功能。',
});
function openDrawer() {
state.isDrawer = true;
}
function closeDrawer() {
state.isDrawer = false;
}
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
message.success('添加成功');
handleReset();
closeDrawer();
} else {
message.error('请填写完整信息');
}
});
}
function handleReset() {
formRef.value.restoreValidation();
state.formParams = Object.assign(state.formParams, defaultValueRef());
}
return {
...toRefs(state),
formRef,
rules,
formSubmit,
handleReset,
openDrawer,
closeDrawer,
};
}, },
}); });
const message = useMessage();
const formRef: any = ref(null);
const defaultValueRef = () => ({
label: '',
type: 1,
subtitle: '',
openType: 1,
auth: '',
path: '',
hidden: false,
});
const formParams = ref(defaultValueRef());
const state = reactive({
isDrawer: false,
subLoading: false,
placement: 'right' as const,
});
function openDrawer() {
state.isDrawer = true;
}
function closeDrawer() {
state.isDrawer = false;
}
function formSubmit() {
formRef.value.validate((errors) => {
if (!errors) {
message.success('添加成功');
handleReset();
closeDrawer();
} else {
message.error('请填写完整信息');
}
});
}
function handleReset() {
formRef.value.restoreValidation();
formParams.value = Object.assign(formParams.value, defaultValueRef());
}
</script> </script>