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
## 1.9.1
- 优化 `typeSctipt` 类型定义
- 优化 `setup` 语法
- 依赖升级
## 1.9.0
- 新增 `BasicForm` 组件,支持 `setLoading`, `setSchema` 方法

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -54,8 +54,8 @@
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from 'vue';
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { useMessage } from 'naive-ui';
import { basicModal, useModal } from '@/components/Modal';
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
@@ -205,90 +205,62 @@
},
];
export default defineComponent({
components: { basicModal, BasicForm },
setup() {
const modalRef: any = ref(null);
const message = useMessage();
const modalRef: any = ref(null);
const message = useMessage();
const [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({
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 [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({
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,
});
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>
<style lang="less">

View File

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

View File

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

View File

@@ -1,111 +1,108 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, Ref } from 'vue';
<script lang="ts" setup>
import { onMounted, ref, Ref } from 'vue';
import { useECharts } from '@/hooks/web/useECharts';
import { basicProps } from './props';
export default defineComponent({
props: basicProps,
setup() {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
defineProps({
...basicProps,
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
onMounted(() => {
setOptions({
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
width: 1,
color: '#019680',
},
onMounted(() => {
setOptions({
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
width: 1,
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,
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,
},
},
],
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
series: [
{
smooth: true,
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',
},
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)'],
},
},
},
},
{
smooth: true,
data: [
33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201, 390,
198, 60, 30, 22, 11,
],
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
series: [
{
smooth: true,
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 };
},
type: 'line',
areaStyle: {},
itemStyle: {
color: '#019680',
},
},
],
});
});
</script>

View File

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

View File

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

View File

@@ -241,11 +241,38 @@
SettingOutlined,
} 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 visits = ref<any>({});
const saleroom = ref<any>({});
const orderLarge = ref<any>({});
const volume = ref(<any>{});
const visits = ref({} as InVisits);
const saleroom = ref({} as InSaleroom);
const orderLarge = ref({} as InOrderLarge);
const volume = ref({} as InVolume);
// 图标列表
const iconList = [

View File

@@ -13,7 +13,7 @@
<n-step title="完成转账" description="恭喜您,转账成功" />
</n-steps>
<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" />
</n-space>
</n-card>
@@ -21,7 +21,7 @@
</template>
<script setup>
import { defineComponent, ref } from 'vue';
import { ref } from 'vue';
import step1 from './Step1.vue';
import step2 from './Step2.vue';
import step3 from './Step3.vue';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
<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-form
:model="formParams"
@@ -38,7 +38,7 @@
<template #footer>
<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-space>
</template>
@@ -46,8 +46,8 @@
</n-drawer>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs } from 'vue';
<script lang="ts" setup>
import { reactive, ref, toRefs } from 'vue';
import { useMessage } from 'naive-ui';
const rules = {
@@ -62,75 +62,58 @@
trigger: 'blur',
},
};
export default defineComponent({
name: 'CreateDrawer',
components: {},
props: {
title: {
type: String,
default: '添加顶级菜单',
},
width: {
type: Number,
default: 450,
},
defineProps({
title: {
type: String,
default: '添加顶级菜单',
},
setup() {
const message = useMessage();
const formRef: any = ref(null);
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,
};
width: {
type: Number,
default: 450,
},
});
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>