feat: 第三方库作为VUE插件进行初始化

master
luoer 2024-01-30 10:02:28 +08:00
parent 95021c503e
commit 7f9cbe8466
19 changed files with 108 additions and 93 deletions

View File

@ -1,6 +1,6 @@
{ {
"tabWidth": 2, "tabWidth": 2,
"printWidth": 120, "printWidth": 180,
"bracketSpacing": true, "bracketSpacing": true,
"singleQuote": true, "singleQuote": true,
"arrowParens": "avoid" "arrowParens": "avoid"

View File

@ -1,7 +1,7 @@
{ {
"name": "starter-vue", "name": "starter-vue",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -22,8 +22,8 @@ addToastInterceptor(api.instance);
* *
*/ */
addExceptionInterceptor(api.instance, () => api.expireHandler?.()); addExceptionInterceptor(api.instance, () => api.expireHandler?.());
/** /**
* *
*/ */
addAuthInterceptor(api.instance); addAuthInterceptor(api.instance);

View File

@ -1,3 +1,4 @@
import { App } from 'vue';
import { Api } from '../generated/Api'; import { Api } from '../generated/Api';
/** /**
@ -9,4 +10,12 @@ export class Service extends Api<unknown> {
* @description * @description
*/ */
expireHandler: () => void = () => {}; expireHandler: () => void = () => {};
/**
* VUE
* @param app
*/
install(app: App) {
app.config.globalProperties.$api = this;
}
} }

View File

@ -5,11 +5,6 @@
<BreadCrumb></BreadCrumb> <BreadCrumb></BreadCrumb>
<div> <div>
<a-link>需要帮助</a-link> <a-link>需要帮助</a-link>
<a-button size="mini" @click="router.push({ path: route.path, query: { s: Math.random() }, force: true })">
<template #icon>
<i class="icon-park-outline-redo"></i>
</template>
</a-button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,12 +1,4 @@
import { import { AnForm, AnFormInstance, AnFormModal, AnFormModalInstance, AnFormModalProps, AnFormProps, getModel } from '@/components/AnForm';
AnForm,
AnFormInstance,
AnFormModal,
AnFormModalInstance,
AnFormModalProps,
AnFormProps,
getModel,
} from '@/components/AnForm';
import AnEmpty from '@/components/AnEmpty/AnEmpty.vue'; import AnEmpty from '@/components/AnEmpty/AnEmpty.vue';
import { Button, PaginationProps, Table, TableColumnData, TableData, TableInstance } from '@arco-design/web-vue'; import { Button, PaginationProps, Table, TableColumnData, TableData, TableInstance } from '@arco-design/web-vue';
import { isArray, isFunction, merge } from 'lodash-es'; import { isArray, isFunction, merge } from 'lodash-es';
@ -14,14 +6,8 @@ import { InjectionKey, PropType, Ref, VNodeChild, defineComponent, ref } from 'v
import { PluginContainer } from '../hooks/useTablePlugin'; import { PluginContainer } from '../hooks/useTablePlugin';
type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>; type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
export type ArcoTableProps = Omit<TableInstance['$props'], 'ref' | 'pagination' | 'loading' | 'data' | 'onPageChange' | 'onPageSizeChange'>;
export type ArcoTableProps = Omit<
TableInstance['$props'],
'ref' | 'pagination' | 'loading' | 'data' | 'onPageChange' | 'onPageSizeChange'
>;
export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>; export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>;
export type TableColumnRender = (data: { record: TableData; column: TableColumnData; rowIndex: number }) => VNodeChild; export type TableColumnRender = (data: { record: TableData; column: TableColumnData; rowIndex: number }) => VNodeChild;
/** /**
@ -207,9 +193,7 @@ export const AnTable = defineComponent({
<div class="an-table table w-full"> <div class="an-table table w-full">
<div class={`mb-3 flex gap-2 toolbar justify-between`}> <div class={`mb-3 flex gap-2 toolbar justify-between`}>
{this.create && <AnFormModal {...this.create} ref="createRef" onSubmited={this.reload}></AnFormModal>} {this.create && <AnFormModal {...this.create} ref="createRef" onSubmited={this.reload}></AnFormModal>}
{this.modify && ( {this.modify && <AnFormModal {...this.modify} trigger={false} ref="modifyRef" onSubmited={this.refresh}></AnFormModal>}
<AnFormModal {...this.modify} trigger={false} ref="modifyRef" onSubmited={this.refresh}></AnFormModal>
)}
{this.$slots.action?.(this.renderData)} {this.$slots.action?.(this.renderData)}
{this.pluginer?.actions && ( {this.pluginer?.actions && (
<div class={`flex-1 flex gap-2 items-center`}> <div class={`flex-1 flex gap-2 items-center`}>
@ -220,12 +204,7 @@ export const AnTable = defineComponent({
)} )}
{this.search && ( {this.search && (
<div> <div>
<AnForm <AnForm ref="searchRef" v-model:model={this.search.model} items={this.search.items} formProps={this.search.formProps}>
ref="searchRef"
v-model:model={this.search.model}
items={this.search.items}
formProps={this.search.formProps}
>
{{ {{
submit: () => ( submit: () => (
<Button type="primary" loading={this.loading} onClick={this.reload}> <Button type="primary" loading={this.loading} onClick={this.reload}>
@ -279,10 +258,7 @@ export type AnTableInstance = InstanceType<typeof AnTable>;
/** /**
* *
*/ */
export type AnTableProps = Pick< export type AnTableProps = Pick<AnTableInstance['$props'], 'source' | 'columns' | 'search' | 'paging' | 'create' | 'modify' | 'tableProps' | 'pluginer'>;
AnTableInstance['$props'],
'source' | 'columns' | 'search' | 'paging' | 'create' | 'modify' | 'tableProps' | 'pluginer'
>;
export interface AnTableContext { export interface AnTableContext {
/** /**

View File

@ -0,0 +1,16 @@
import { ButtonProps, TableData } from '@arco-design/web-vue';
export interface AnTableActionBase {
text: string;
icon: string | Component;
visible: () => boolean;
disable: () => boolean;
buttonProps: ButtonProps;
}
interface AnTableActionBatch {
type: 'batch';
onClick: (rows: TableData) => void;
}
export type AnTableAction = AnTableActionBase & AnTableActionBatch;

View File

@ -35,6 +35,16 @@ export interface TableUseOptions extends Pick<AnTableProps, 'source' | 'tablePro
* ``` * ```
*/ */
columns?: TableColumn[]; columns?: TableColumn[];
/**
*
* @example
* ```ts
* [{
* text: '按钮',
* onClick: () => null,
* }]
* ```
*/
actions?: any[]; actions?: any[];
/** /**
* *

View File

@ -2,22 +2,7 @@ import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-cn';
import localData from 'dayjs/plugin/localeData'; import localData from 'dayjs/plugin/localeData';
import relativeTime from 'dayjs/plugin/relativeTime'; import relativeTime from 'dayjs/plugin/relativeTime';
import { App } from 'vue';
/**
*
*
*/
const DATETIME = 'YYYY-MM-DD HH:mm';
/**
*
*/
const DATE = 'YYYY-MM-DD';
/**
*
*/
const TIME = 'HH:mm:ss';
/** /**
* *
@ -63,4 +48,11 @@ dayjs.prototype.format = function (format: string = dayjs.DATETIME) {
return this._format(format); return this._format(format);
}; };
export { DATE, DATETIME, TIME, dayjs }; /**
* VUE
*/
dayjs.install = function dayjsPlugin(app: App) {
app.config.globalProperties.$dayjs = dayjs;
};
export { dayjs };

View File

@ -1,4 +1,5 @@
import 'dayjs'; import 'dayjs';
import { App } from 'vue';
declare module 'dayjs' { declare module 'dayjs' {
/** /**
@ -13,4 +14,6 @@ declare module 'dayjs' {
interface Dayjs { interface Dayjs {
_format: Dayjs['format']; _format: Dayjs['format'];
} }
export var install: (app: App) => void;
} }

View File

@ -1,6 +1,7 @@
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import 'nprogress/nprogress.css'; import 'nprogress/nprogress.css';
import './nprogress.css'; import './nprogress.css';
import { App } from 'vue';
NProgress.configure({ NProgress.configure({
showSpinner: false, showSpinner: false,
@ -8,5 +9,9 @@ NProgress.configure({
minimum: 0.3, minimum: 0.3,
}); });
export { NProgress }; /**
* VUE
*/
NProgress.install = function (app: App) {};
export { NProgress };

8
src/libs/nprogress/interface.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
import 'nprogress';
import { App } from 'vue';
declare module 'nprogress' {
interface NProgress {
install: (app: App) => void;
}
}

View File

@ -1,12 +1,18 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import App from './App.vue'; import App from '@/App.vue';
import { router } from './router'; import { router } from '@/router';
import { store } from './store'; import { store } from '@/store';
import { style } from './styles'; import { style } from '@/styles';
import { dayjs } from '@/libs/dayjs';
import { NProgress } from '@/libs/nprogress';
import { api } from '@/api';
const run = async () => { const run = async () => {
const app = createApp(App); const app = createApp(App);
app.use(dayjs);
app.use(NProgress);
app.use(store); app.use(store);
app.use(api);
app.use(style); app.use(style);
app.use(router); app.use(router);
await router.isReady(); await router.isReady();

View File

@ -42,7 +42,17 @@ export default defineComponent({
} }
return ( return (
<> <>
<a-menu-item key={route.path} v-slots={{ icon }} onClick={() => goto(route)}> <a-menu-item key={route.path} v-slots={{ icon }}>
{route.link ? (
<div class="flex items-center justify-between gap-2" onClick={() => goto(route)}>
<div>{route.title}</div>
<div class="text-xs text-gray-400">
{/* <a-badge count={8}>8</a-badge> */}
{route.hide === 'prod' ? <a-tag color="red">{'开发'}</a-tag> : null}
</div>
</div>
) : (
<router-link to={route.path}>
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<div>{route.title}</div> <div>{route.title}</div>
<div class="text-xs text-gray-400"> <div class="text-xs text-gray-400">
@ -50,6 +60,8 @@ export default defineComponent({
{route.hide === 'prod' ? <a-tag color="red">{'开发'}</a-tag> : null} {route.hide === 'prod' ? <a-tag color="red">{'开发'}</a-tag> : null}
</div> </div>
</div> </div>
</router-link>
)}
</a-menu-item> </a-menu-item>
</> </>
); );

View File

@ -1,8 +1,6 @@
<template> <template>
<a-layout class="layout"> <a-layout class="layout">
<a-layout-header <a-layout-header class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700">
class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700"
>
<div class="h-13 flex items-center"> <div class="h-13 flex items-center">
<!-- <a-button size="small" @click="isCollapsed = !isCollapsed"> <!-- <a-button size="small" @click="isCollapsed = !isCollapsed">
<template #icon> <template #icon>
@ -19,18 +17,6 @@
</router-link> </router-link>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div>
<a-input-search placeholder="搜索菜单/页面" :allow-clear="true"></a-input-search>
</div>
<a-tooltip content="上传文件">
<a-button @click="() => null" class="!bg-transparent !hover:bg-gray-100">
<template #icon>
<a-badge :count="1" :dot="true">
<i class="text-base icon-park-outline-upload-one"></i>
</a-badge>
</template>
</a-button>
</a-tooltip>
<a-tooltip v-for="btn in buttons" :key="btn.icon" :content="btn.tooltip"> <a-tooltip v-for="btn in buttons" :key="btn.icon" :content="btn.tooltip">
<a-button @click="btn.onClick" class="!bg-transparent !hover:bg-gray-100"> <a-button @click="btn.onClick" class="!bg-transparent !hover:bg-gray-100">
<template #icon> <template #icon>
@ -90,9 +76,9 @@ import { useAppStore } from '@/store/app';
import { useMenuStore } from '@/store/menu'; import { useMenuStore } from '@/store/menu';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { IconSync } from '@arco-design/web-vue/es/icon'; import { IconSync } from '@arco-design/web-vue/es/icon';
import { useFullscreen } from '@vueuse/core';
import Menu from './Menu.vue'; import Menu from './Menu.vue';
import userDropdown from './UserDropdown.vue'; import userDropdown from './UserDropdown.vue';
import { useFullscreen } from '@vueuse/core';
defineOptions({ name: 'LayoutPage' }); defineOptions({ name: 'LayoutPage' });

View File

@ -2,10 +2,6 @@
<AnPage></AnPage> <AnPage></AnPage>
</template> </template>
<script setup lang="tsx"></script>
<style lang="less"></style>
<route lang="json"> <route lang="json">
{ {
"redirect": "/setting/common", "redirect": "/setting/common",

View File

@ -1,4 +1,4 @@
import { NProgress } from '@/libs/nprogress'; import NProgress from 'nprogress';
import { useAppStore } from '@/store/app'; import { useAppStore } from '@/store/app';
import { Router } from 'vue-router'; import { Router } from 'vue-router';

View File

@ -78,10 +78,10 @@ body {
background-color: var(--color-neutral-2); background-color: var(--color-neutral-2);
} }
&.arco-menu-selected { &.arco-menu-selected {
color: @arcoblue-6; // color: @arcoblue-6;
background-color: rgb(var(--primary-1)); // background-color: rgb(var(--primary-1));
// color: #fff; color: #fff;
// background-color: rgb(var(--primary-6)); background-color: rgb(var(--primary-6));
.arco-menu-icon { .arco-menu-icon {
color: inherit; color: inherit;
} }

5
src/types/vue.d.ts vendored
View File

@ -1,8 +1,9 @@
import { Router, RouteLocationNormalizedLoaded } from "vue-router"; import { Router, RouteLocationNormalizedLoaded } from 'vue-router';
declare module "vue" { declare module 'vue' {
interface ComponentCustomProperties { interface ComponentCustomProperties {
$router: Router; $router: Router;
$route: RouteLocationNormalizedLoaded; $route: RouteLocationNormalizedLoaded;
$dayjs: Dayjs;
} }
} }