ray template v3.0

This commit is contained in:
chuan_wuhao 2022-12-19 16:45:09 +08:00
parent 9f40069a53
commit 6cb548ae5c
19 changed files with 397 additions and 89 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@ dist/
*.local
# Editor directories and files
.vscode
.vscode/*
!.vscode/extensions.json
.idea

View File

@ -1,30 +1,67 @@
# Ray template
# `Ray Template`
## 前言
> 模板按照个人习惯进行搭建, 可以根据个人喜好进行更改. 预设了一些组件库、国际化库的东西. 建议使用 `naive-ui` 作为组件库.
> 该项目模板采用 `vue3.x` `vite3.2` `tsx` 进行开发,使用 `naive ui` 作为组件库。意在提供一个简洁、快速上手的模板。
## 在线预览
## 版本说明
[在线预览](https://xiaodaigua-ray.github.io/#/)
> 做了一些大的改动升级,让模板更加好用了一点,默认主题色也做了变更更好看了一点。啰嗦两句,好像也没啥其他的了...
## 项目说明
## 功能
> 项目采用 `Vue 3` `TypeScript` `TSX` `Vite` 进行开发, 已经集成了一些常用的开发库, 进行了一些 `Vite` 相关配置, 例如全局自动引入、`GZ` 打包、按需引入打包、[reactivityTransform](https://vuejs.org/guide/extras/reactivity-transform.html)等, 解放你的双手. 国际化插件, 按照项目需求自己取舍. 引入了比较火的 `hook` 库 [@vueuse](https://vueuse.org/), 极大提高你的搬砖效率. `小提醒: 为了避免使用 @vueuse 时出现奇奇怪怪的错误(例如: useDraggable 在使用的时候, TSX 形式开发会失效), 建议采用 <script setup /> 形式进行开发`. 可以根据自己项目实际需求进行配置 `px` 与 'rem' 转换比例(使用 `postcss-pxtorem``autoprefixer` 实现).
- 主题切换
- 错误页
- 封装了一些小组件
- 还有一些不值一提的小东西...
> 项目已经预设了一些打包优化, 例如: 压缩, `base64` 转换, 按需打包. 但是值得注意的是, 禁止全局导入使用 `lodash-es` 这样会破坏按需打包.
## 预览地址
> 项目暂时没有揉合乱七八糟的库, 仅仅是为了作为一个顺手的工具, 意在提供一个干净, 简单的脚手架.
[**`点击预览`**](https://xiaodaigua-ray.github.io/#/)
## 拉取依赖
```
# yarn
yarn
```
```
# npm
npm install
```
## 启动项目
`yarn dev` / `npm run dev`
```
# yarn
yarn dev
```
```
# npm
npm run dev
```
## 项目打包
`yarn build` / `npm run build`
```
# yarn
## 使用开源库
yarn build
```
```
# npm
npm run build
```
## 项目依赖
- [pinia](https://pinia.vuejs.org/) `全局状态管理器`
- [@vueuse](https://vueuse.org/) `vue3 hooks`
@ -36,25 +73,21 @@
- [vite-svg-loader](https://github.com/jpkleemans/vite-svg-loader) `svg组件化`
- [vite-plugin-svg-icons](https://github.com/vbenjs/vite-plugin-svg-icons/blob/main/README.zh_CN.md) `svg雪碧图`
- [echarts5](https://echarts.apache.org/examples/zh/index.html#chart-type-line) `可视化`
- [lodash](https://www.lodashjs.com/) `拓展方法`
- [lodash-es](https://www.lodashjs.com/) `拓展方法`
## 基础组件
`RayScrollReveal` 基于 `ScrollReveal` 进行开发, 可以实现滚动加载动画(暂时移除)
`RayTransitionComponent` 路由过渡动画组件, 可根据自己喜好更改 `src/styles/animate.scss` 文件过渡效果
`RayChart` 基于 `echarts5` 封装, 可根据自己实际需求进行拓展
`RayIcon` `svg` 小图标组件
- `RayIcon` `svg icon`
- `RayChart` 基于 `echarts5.x` 封装可视化组件
- `RayTransitionComponent` 带过渡动画路由组件,效果与 `RouterView` 相同
- `RayTable` 基于 `Naive UI DataTable` 组件封装,实现了一些小功能
## 项目结构
```
- locales: 国际化多语言入口(本项目采用 `json` 格式)
- locales: 国际化多语言入口(本项目采用 json 格式)
- assets: 项目静态资源入口
- images: 项目图片资源
- component: 全局共用组件
@ -67,32 +100,20 @@
- router: 路由表
- store: 全局状态管理入口
- modules
- setting: demo
- styles: 全局公共样式入口
- types: 全局 type
- utils: 工具包
- cache: 缓存方法
- crypto: 常用的加密方法
- element: dom 相关操作方法
- hook: 常用 hook 方法
- views: 页面入口
- vite-plugin: 插件注册
```
## 如果你采用的 `naive-ui` 作为组件库, 可能需要它
## 浏览器支持
```
# 如何在项目内使用提示组件
window.$dialog
window.$message
window.$loadingBar
window.$notification
```
> 仅支持现代浏览器,不支持 `IE`
### 祝大家搬砖愉快, 希望这个模板能帮你省很多时间
## 最后,希望大家搬砖愉快

View File

@ -15,6 +15,7 @@
"amfe-flexible": "^2.2.1",
"axios": "^1.2.0",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.7",
"echarts": "^5.4.0",
"lodash-es": "^4.17.21",
"naive-ui": "^2.34.0",
@ -25,7 +26,8 @@
"vue": "^3.2.37",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.3",
"vuedraggable": "^4.1.0"
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@babel/core": "^7.20.2",

View File

@ -0,0 +1,76 @@
/**
*
* @author Ray <https://github.com/XiaoDaiGua-Ray>
*
* @date 2022-12-19
*
* @workspace ray-template
*
* @remark
*/
import { NPopconfirm, NSpace, NButton } from 'naive-ui'
import RayIcon from '@/components/RayIcon/index'
import type { ExportExcelProvider } from '@/components/RayTable/src/type'
const ExportExcel = defineComponent({
name: 'ExportExcel',
emits: ['exportPositive', 'exportNegative'],
setup(_, { emit }) {
const exportExcelProvider = inject(
'exportExcelProvider',
{} as ExportExcelProvider,
)
const showPopoconfirm = ref(false)
const handleButtonClick = (type: string) => {
type === 'positive' ? emit('exportPositive') : emit('exportNegative')
showPopoconfirm.value = false
}
return {
...exportExcelProvider,
handleButtonClick,
showPopoconfirm,
}
},
render() {
return (
<NPopconfirm v-model:show={this.showPopoconfirm}>
{{
trigger: () => (
<RayIcon
name="export_excel"
size="18"
customClassName="ray-table-icon"
/>
),
default: () => this.exportTip,
action: () => (
<NSpace>
<NButton
size="small"
ghost
onClick={this.handleButtonClick.bind(this, 'negative')}
>
{this.exportNegativeText}
</NButton>
<NButton
size="small"
ghost
type="info"
onClick={this.handleButtonClick.bind(this, 'positive')}
>
{this.exportPositiveText}
</NButton>
</NSpace>
),
}}
</NPopconfirm>
)
},
})
export default ExportExcel

View File

@ -7,9 +7,17 @@ $activedColor: #2080f0;
transition: transform 0.3s var(--r-bezier);
}
.table-setting__card {
padding: 12px 8px;
& .n-card__content {
padding: 0 !important;
margin: 0 !important;
}
}
.ray-table__setting-option--draggable {
display: grid;
grid-template-columns: repeat(1, $width);
grid-row-gap: 10px;
justify-items: center;
align-items: center;
@ -19,8 +27,19 @@ $activedColor: #2080f0;
display: flex;
align-items: center;
cursor: pointer;
padding: 8px 10px;
border-radius: 2px;
transition: background-color 0.3s var(--r-bezier);
&.draggable-item--dark {
&:hover {
background-color: rgba(255, 255, 255, 0.08);
}
}
&:hover {
background-color: #e8f2fd;
& .draggable-item__d--icon {
opacity: 1;
}
@ -49,8 +68,8 @@ $activedColor: #2080f0;
}
& .n-ellipsis {
max-width: 71px;
min-width: 71px;
max-width: 80px;
min-width: 80px;
}
}
}

View File

@ -10,13 +10,14 @@
*/
import './index.scss'
import { NCard, NPopover, NEllipsis, NCheckbox } from 'naive-ui'
import { NCard, NPopover, NEllipsis, NButton } from 'naive-ui'
import RayIcon from '@/components/RayIcon/index'
import VueDraggable from 'vuedraggable'
import { setupSettingOptions } from './hook'
import { useSetting } from '@/store'
import type {
RayTableProvider,
TableSettingProvider,
ActionOptions,
FixedType,
TableSettingFixedPopoverIcon,
@ -26,11 +27,16 @@ const TableSetting = defineComponent({
name: 'TableSetting',
emits: ['columnsUpdate'],
setup(_, { emit }) {
const rayTableProvider = inject('rayTableProvider', {} as RayTableProvider)
const settingStore = useSetting()
const tableSettingProvider = inject(
'tableSettingProvider',
{} as TableSettingProvider,
)
const settingOptions = ref(
setupSettingOptions(rayTableProvider.modelColumns.value),
setupSettingOptions(tableSettingProvider.modelColumns.value),
) // 表格表头
const disableDraggable = ref(true) // 拖拽开关
const disableDraggable = ref(true) // 拖拽开关(暂时弃用)
const { themeValue } = storeToRefs(settingStore)
const handleDraggableEnd = () => {
emit('columnsUpdate', settingOptions.value)
@ -57,6 +63,7 @@ const TableSetting = defineComponent({
</NPopover>
)
}
/**
*
* @param type
@ -111,6 +118,7 @@ const TableSetting = defineComponent({
disableDraggable,
FixedPopoverIcon,
handleResizeColumnClick,
themeValue,
}
},
render() {
@ -125,7 +133,7 @@ const TableSetting = defineComponent({
/>
),
default: () => (
<NCard bordered={false} segmented={{ content: 'soft' }}>
<NCard bordered={false} class="table-setting__card">
{{
default: () => (
<VueDraggable
@ -143,7 +151,12 @@ const TableSetting = defineComponent({
element: ActionOptions
index: number
}) => (
<div class={['draggable-item']}>
<div
class={[
'draggable-item',
this.themeValue ? 'draggable-item--dark' : '',
]}
>
<RayIcon
customClassName={`draggable-item__d--icon`}
name="draggable"
@ -195,11 +208,6 @@ const TableSetting = defineComponent({
}}
</VueDraggable>
),
header: () => (
<NCheckbox v-model:checked={this.disableDraggable}>
</NCheckbox>
),
}}
</NCard>
),

View File

@ -0,0 +1,12 @@
import type { ExportExcelHeader } from './type'
import type { DataTableColumns } from 'naive-ui'
export const setupExportHeader = (columns: ExportExcelHeader[]) => {
const header = columns.reduce((pre, curr) => {
pre[curr.key] = curr.title
return pre
}, {} as ExportExcelHeader)
return header
}

View File

@ -1,4 +1,5 @@
.ray-table__setting {
.ray-table__setting,
.ray-table-icon {
cursor: pointer;
outline: none;
border: none;

View File

@ -10,18 +10,22 @@
*/
import './index.scss'
import { NDataTable, NCard, NDropdown } from 'naive-ui'
import { NDataTable, NCard, NDropdown, NSpace } from 'naive-ui'
import props from './props'
import TableSetting from './components/TableSetting/index'
import ExportExcel from './components/ExportExcel/index'
import { utils, writeFileXLSX } from 'xlsx'
import dayjs from 'dayjs'
import { setupExportHeader } from './hook'
import type { ActionOptions } from './type'
import type { ActionOptions, ExportExcelHeader } from './type'
import type { WritableComputedRef } from 'vue'
import type { DropdownOption } from 'naive-ui'
const RayTable = defineComponent({
name: 'RayTable',
props: props,
emits: ['update:columns', 'menuSelect'],
emits: ['update:columns', 'menuSelect', 'exportSuccess', 'exportError'],
setup(props, { emit }) {
const modelRightClickMenu = computed(() => props.rightClickMenu)
const modelColumns = computed({
@ -37,10 +41,17 @@ const RayTable = defineComponent({
})
let prevRightClickIndex = -1
provide('rayTableProvider', {
provide('tableSettingProvider', {
modelRightClickMenu,
modelColumns,
})
provide('exportExcelProvider', {
exportTip: props.exportTip,
exportType: props.exportType,
exportPositiveText: props.exportPositiveText,
exportNegativeText: props.exportNegativeText,
exportFilename: props.exportFilename,
})
const handleColumnsUpdate = (arr: ActionOptions[]) => {
modelColumns.value = arr
@ -92,11 +103,48 @@ const RayTable = defineComponent({
}
}
const handleExportPositive = () => {
if (props.data.length && props.columns.length) {
try {
const exportHeader = setupExportHeader(
props.columns as ExportExcelHeader[],
) // 获取所有列(设置为 `excel` 表头)
const sheetData = utils.json_to_sheet(props.data)
const workBook = utils.book_new()
const filename = props.exportFilename
? props.exportFilename + '.xlsx'
: dayjs(new Date()).format('YYYY-MM-DD') + '.xlsx'
utils.book_append_sheet(workBook, sheetData, 'Data')
const range = utils.decode_range(sheetData['!ref'] as string) // 获取所有单元格
/**
*
*
*
* ,
*/
for (let c = range.s.c; c <= range.e.c; c++) {
const header = utils.encode_col(c) + '1'
sheetData[header].v = exportHeader[sheetData[header].v]
}
writeFileXLSX(workBook, filename) // 输出表格
emit('exportSuccess')
} catch (e) {
emit('exportError')
}
}
}
return {
handleColumnsUpdate,
...toRefs(menuConfig),
handleRowProps,
handleRightMenuSelect,
handleExportPositive,
}
},
render() {
@ -133,9 +181,14 @@ const RayTable = defineComponent({
header: () => this.title,
'header-extra': () =>
this.action ? (
<TableSetting
onColumnsUpdate={this.handleColumnsUpdate.bind(this)}
/>
<NSpace align="center">
<ExportExcel
onExportPositive={this.handleExportPositive.bind(this)}
/>
<TableSetting
onColumnsUpdate={this.handleColumnsUpdate.bind(this)}
/>
</NSpace>
) : (
''
),

View File

@ -68,6 +68,54 @@ const rayTableProps = {
type: Boolean,
default: true,
},
exportTip: {
/**
*
*
*/
type: String,
default: '是否导出为excel',
},
exportType: {
/**
*
*
*
* `xlsx`
*
* `xlsx`
*/
type: String,
default: 'xlsx',
},
exportPositiveText: {
/**
*
*
*
* `确认`
*/
type: String,
default: '确认',
},
exportNegativeText: {
/**
*
*
*
* `取消`
*/
type: String,
default: '取消',
},
exportFilename: {
/**
*
*
*/
type: String,
default: '',
},
} as const
export default rayTableProps

View File

@ -5,7 +5,7 @@ import type {
DropdownRenderOption,
DataTableBaseColumn,
} from 'naive-ui'
import type { ComputedRef, WritableComputedRef } from 'vue'
import type { ComputedRef, WritableComputedRef, VNode } from 'vue'
export interface ActionOptions extends DataTableBaseColumn {
leftFixedActivated?: boolean // 向左固定
@ -35,7 +35,38 @@ export type SettingOptions = WritableComputedRef<ActionOptions[]>
export type RightClickMenu = ComputedRef<DropdownMixedOption[]>
export interface RayTableProvider {
export interface TableSettingProvider {
modelRightClickMenu: RightClickMenu
modelColumns: SettingOptions
}
export interface ExportExcelProvider {
exportTip: string
exportType: string
exportPositiveText: string
exportNegativeText: string
exportFilename: string
}
export type ColumnKey = string | number
declare type VNodeChildAtom =
| VNode
| string
| number
| boolean
| null
| undefined
| void
export declare type VNodeArrayChildren = Array<
VNodeArrayChildren | VNodeChildAtom
>
export declare type VNodeChild = VNodeChildAtom | VNodeArrayChildren
export declare type TableColumnTitle =
| string
| ((column: DataTableBaseColumn) => VNodeChild)
export interface ExportExcelHeader extends DataTableBaseColumn {}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,7 +1,7 @@
export const useSwatchesColorOptions = () => [
'#FFFFFF',
'#18A058',
'#2080F0',
'#2d8cf0',
'#F0A020',
'rgba(208, 48, 80, 1)',
]

View File

@ -69,7 +69,7 @@ export const useMenu = defineStore('menu', () => {
}
}
matchMenuItem(menuState.options)
matchMenuItem(menuState.options as MenuOption[])
}
/**

View File

@ -7,7 +7,7 @@ export const useSetting = defineStore(
drawerPlacement: 'right' as NaiveDrawerPlacement,
primaryColorOverride: {
common: {
primaryColor: '#18A058', // 主题色
primaryColor: '#2d8cf0', // 主题色
},
},
themeValue: false, // `true` 为黑夜主题, `false` 为白色主题

View File

@ -151,12 +151,12 @@ declare global {
private id
private eventObj
constructor(id: string)
$on(event: string, fn: callback): EventBus
$on(event: string, fn: Function): EventBus
/** 任何$emit都会导致监听函数触发第一个参数为事件名后续的参数为$emit的参数 */
$onAll(fn: (event: string, ...args: Array<unknown>) => unknown): EventBus
$once(event: string, fn: callback): void
$off(event: string, fn: callback): EventBus
$offAll(fn: callback): EventBus
$once(event: string, fn: Function): void
$off(event: string, fn: Function): EventBus
$offAll(fn: Function): EventBus
$emit(event: string, ...args: Array<unknown>): EventBus
$clear(): EventBus
}

View File

@ -38,16 +38,26 @@ export const removeCache = (
key: string | 'all' | 'all-sessionStorage' | 'all-localStorage',
type: CacheType = 'sessionStorage',
) => {
if (key === 'all') {
window.window.localStorage.clear()
window.sessionStorage.clear()
} else if (key === 'all-sessionStorage') {
window.sessionStorage.clear()
} else if (key === 'all-localStorage') {
window.localStorage.clear()
} else {
type === 'localStorage'
? window.localStorage.removeItem(key)
: window.sessionStorage.removeItem(key)
switch (key) {
case 'all':
window.window.localStorage.clear()
window.sessionStorage.clear()
break
case 'all-sessionStorage':
window.sessionStorage.clear()
break
case 'all-localStorage':
window.localStorage.clear()
break
default:
type === 'localStorage'
? window.localStorage.removeItem(key)
: window.sessionStorage.removeItem(key)
}
}

View File

@ -33,7 +33,9 @@ const RelyAbout = defineComponent({
key: 'relyAddress',
},
]
const relyData = ref<RelyDataOptions[]>([])
const dependenciesOptions = ref<RelyDataOptions[]>([])
const devDependenciesOptions = ref<RelyDataOptions[]>([])
const templateOptions = [
{
name: '项目名称',
@ -62,10 +64,8 @@ const RelyAbout = defineComponent({
return pre
}, [] as RelyDataOptions[])
const arrDependencies = _arrayFrom(dependencies)
const arrDevDependencies = _arrayFrom(devDependencies)
relyData.value = [...arrDependencies, ...arrDevDependencies]
dependenciesOptions.value = _arrayFrom(dependencies)
devDependenciesOptions.value = _arrayFrom(devDependencies)
}
const handleTagClick = (item: TemplateOptions) => {
@ -80,7 +80,8 @@ const RelyAbout = defineComponent({
return {
columns,
relyData,
dependenciesOptions,
devDependenciesOptions,
templateOptions,
handleTagClick,
}
@ -110,9 +111,18 @@ const RelyAbout = defineComponent({
))}
</NDescriptions>
</NCard>
<NCard title="项目依赖">
<NCard title="生产依赖">
<NDescriptions bordered labelPlacement="left">
{this.relyData.map((curr) => (
{this.dependenciesOptions.map((curr) => (
<NDescriptionsItem key={curr.name} label={curr.name}>
{curr.relyVersion}
</NDescriptionsItem>
))}
</NDescriptions>
</NCard>
<NCard title="开发依赖">
<NDescriptions bordered labelPlacement="left">
{this.devDependenciesOptions.map((curr) => (
<NDescriptionsItem key={curr.name} label={curr.name}>
{curr.relyVersion}
</NDescriptionsItem>

View File

@ -131,7 +131,7 @@ const TableView = defineComponent({
<NCard title="RayTable">
<p>
Naive UI DataTable . , ,
excel
</p>
<p>RayTable DataTable </p>
<p>
@ -158,6 +158,7 @@ const TableView = defineComponent({
<p></p>
<p>, </p>
<p>, </p>
<p> excel , </p>
</div>
),
default: () => (