Initial commit
49
promo-ui2/src/App.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { ConfigGlobal } from '@/components/ConfigGlobal'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('app')
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const currentSize = computed(() => appStore.getCurrentSize)
|
||||
|
||||
const greyMode = computed(() => appStore.getGreyMode)
|
||||
|
||||
appStore.initTheme()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ConfigGlobal :size="currentSize">
|
||||
<RouterView :class="greyMode ? `${prefixCls}-grey-mode` : ''" />
|
||||
</ConfigGlobal>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{adminNamespace}-app';
|
||||
|
||||
.size {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0 !important;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
.size;
|
||||
|
||||
#app {
|
||||
.size;
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls}-grey-mode {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
</style>
|
||||
11
promo-ui2/src/api/common/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import request from '@/axios'
|
||||
|
||||
// 获取所有字典
|
||||
export const getDictApi = () => {
|
||||
return request.get({ url: '/mock/dict/list' })
|
||||
}
|
||||
|
||||
// 模拟获取某个字典
|
||||
export const getDictOneApi = async () => {
|
||||
return request.get({ url: '/mock/dict/one' })
|
||||
}
|
||||
23
promo-ui2/src/api/dashboard/analysis/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import request from '@/axios'
|
||||
import type {
|
||||
AnalysisTotalTypes,
|
||||
UserAccessSource,
|
||||
WeeklyUserActivity,
|
||||
MonthlySales
|
||||
} from './types'
|
||||
|
||||
export const getCountApi = (): Promise<IResponse<AnalysisTotalTypes[]>> => {
|
||||
return request.get({ url: '/mock/analysis/total' })
|
||||
}
|
||||
|
||||
export const getUserAccessSourceApi = (): Promise<IResponse<UserAccessSource[]>> => {
|
||||
return request.get({ url: '/mock/analysis/userAccessSource' })
|
||||
}
|
||||
|
||||
export const getWeeklyUserActivityApi = (): Promise<IResponse<WeeklyUserActivity[]>> => {
|
||||
return request.get({ url: '/mock/analysis/weeklyUserActivity' })
|
||||
}
|
||||
|
||||
export const getMonthlySalesApi = (): Promise<IResponse<MonthlySales[]>> => {
|
||||
return request.get({ url: '/mock/analysis/monthlySales' })
|
||||
}
|
||||
22
promo-ui2/src/api/dashboard/analysis/types.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export type AnalysisTotalTypes = {
|
||||
users: number
|
||||
messages: number
|
||||
moneys: number
|
||||
shoppings: number
|
||||
}
|
||||
|
||||
export type UserAccessSource = {
|
||||
value: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export type WeeklyUserActivity = {
|
||||
value: number
|
||||
name: string
|
||||
}
|
||||
|
||||
export type MonthlySales = {
|
||||
name: string
|
||||
estimate: number
|
||||
actual: number
|
||||
}
|
||||
22
promo-ui2/src/api/dashboard/workplace/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import request from '@/axios'
|
||||
import type { WorkplaceTotal, Project, Dynamic, Team, RadarData } from './types'
|
||||
|
||||
export const getCountApi = (): Promise<IResponse<WorkplaceTotal>> => {
|
||||
return request.get({ url: '/mock/workplace/total' })
|
||||
}
|
||||
|
||||
export const getProjectApi = (): Promise<IResponse<Project>> => {
|
||||
return request.get({ url: '/mock/workplace/project' })
|
||||
}
|
||||
|
||||
export const getDynamicApi = (): Promise<IResponse<Dynamic[]>> => {
|
||||
return request.get({ url: '/mock/workplace/dynamic' })
|
||||
}
|
||||
|
||||
export const getTeamApi = (): Promise<IResponse<Team[]>> => {
|
||||
return request.get({ url: '/mock/workplace/team' })
|
||||
}
|
||||
|
||||
export const getRadarApi = (): Promise<IResponse<RadarData[]>> => {
|
||||
return request.get({ url: '/mock/workplace/radar' })
|
||||
}
|
||||
30
promo-ui2/src/api/dashboard/workplace/types.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export type WorkplaceTotal = {
|
||||
project: number
|
||||
access: number
|
||||
todo: number
|
||||
}
|
||||
|
||||
export type Project = {
|
||||
name: string
|
||||
icon: string
|
||||
message: string
|
||||
personal: string
|
||||
time: Date | number | string
|
||||
}
|
||||
|
||||
export type Dynamic = {
|
||||
keys: string[]
|
||||
time: Date | number | string
|
||||
}
|
||||
|
||||
export type Team = {
|
||||
name: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
export type RadarData = {
|
||||
personal: number
|
||||
team: number
|
||||
max: number
|
||||
name: string
|
||||
}
|
||||
30
promo-ui2/src/api/department/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import request from '@/axios'
|
||||
import { DepartmentListResponse, DepartmentUserParams, DepartmentUserResponse } from './types'
|
||||
|
||||
export const getDepartmentApi = () => {
|
||||
return request.get<DepartmentListResponse>({ url: '/mock/department/list' })
|
||||
}
|
||||
|
||||
export const getUserByIdApi = (params: DepartmentUserParams) => {
|
||||
return request.get<DepartmentUserResponse>({ url: '/mock/department/users', params })
|
||||
}
|
||||
|
||||
export const deleteUserByIdApi = (ids: string[] | number[]) => {
|
||||
return request.post({ url: '/mock/department/user/delete', data: { ids } })
|
||||
}
|
||||
|
||||
export const saveUserApi = (data: any) => {
|
||||
return request.post({ url: '/mock/department/user/save', data })
|
||||
}
|
||||
|
||||
export const saveDepartmentApi = (data: any) => {
|
||||
return request.post({ url: '/mock/department/save', data })
|
||||
}
|
||||
|
||||
export const deleteDepartmentApi = (ids: string[] | number[]) => {
|
||||
return request.post({ url: '/mock/department/delete', data: { ids } })
|
||||
}
|
||||
|
||||
export const getDepartmentTableApi = (params: any) => {
|
||||
return request.get({ url: '/mock/department/table/list', params })
|
||||
}
|
||||
32
promo-ui2/src/api/department/types.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export interface DepartmentItem {
|
||||
id: string
|
||||
departmentName: string
|
||||
children?: DepartmentItem[]
|
||||
}
|
||||
|
||||
export interface DepartmentListResponse {
|
||||
list: DepartmentItem[]
|
||||
}
|
||||
|
||||
export interface DepartmentUserParams {
|
||||
pageSize: number
|
||||
pageIndex: number
|
||||
id: string
|
||||
username?: string
|
||||
account?: string
|
||||
}
|
||||
|
||||
export interface DepartmentUserItem {
|
||||
id: string
|
||||
username: string
|
||||
account: string
|
||||
email: string
|
||||
createTime: string
|
||||
role: string
|
||||
department: DepartmentItem
|
||||
}
|
||||
|
||||
export interface DepartmentUserResponse {
|
||||
list: DepartmentUserItem[]
|
||||
total: number
|
||||
}
|
||||
48
promo-ui2/src/api/login/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from '@/axios'
|
||||
import type { ApiSmsBodyType, RegisterUserType, UserType } from './types'
|
||||
|
||||
interface RoleParams {
|
||||
roleName: string
|
||||
}
|
||||
|
||||
export const loginApi = (data: UserType): Promise<IResponse<UserType>> => {
|
||||
return request.post({ url: '/promo/login', data })
|
||||
}
|
||||
|
||||
export const registerApi = (data: RegisterUserType): Promise<IResponse<UserType>> => {
|
||||
return request.post({ url: '/promo/register', data })
|
||||
}
|
||||
|
||||
export const getCodeApi = (phoneNumber: string): Promise<IResponse<UserType>> => {
|
||||
return request.post({
|
||||
url: '/api/sms/getVerifyCode',
|
||||
data: {
|
||||
phoneNumber: phoneNumber,
|
||||
type: '1'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const loginOutApi = (): Promise<IResponse> => {
|
||||
return request.get({ url: '/mock/user/loginOut' })
|
||||
}
|
||||
|
||||
export const getUserListApi = ({ params }: AxiosConfig) => {
|
||||
return request.get<{
|
||||
code: string
|
||||
data: {
|
||||
list: UserType[]
|
||||
total: number
|
||||
}
|
||||
}>({ url: '/mock/user/list', params })
|
||||
}
|
||||
|
||||
export const getAdminRoleApi = (
|
||||
params: RoleParams
|
||||
): Promise<IResponse<AppCustomRouteRecordRaw[]>> => {
|
||||
return request.get({ url: '/mock/role/list', params })
|
||||
}
|
||||
|
||||
export const getTestRoleApi = (params: RoleParams): Promise<IResponse<string[]>> => {
|
||||
return request.get({ url: '/mock/role/list2', params })
|
||||
}
|
||||
28
promo-ui2/src/api/login/types.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export interface UserLoginType {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface UserType {
|
||||
uid: number
|
||||
username: string
|
||||
password: string
|
||||
role: string
|
||||
roleId: string
|
||||
token: string
|
||||
shareLink: string
|
||||
shareDomain: string
|
||||
}
|
||||
|
||||
export interface RegisterUserType {
|
||||
username: string
|
||||
password: string
|
||||
check_password: string
|
||||
contactInformation: string
|
||||
parentInvitationCode: string
|
||||
}
|
||||
|
||||
export interface ApiSmsBodyType {
|
||||
phoneNumber: string;
|
||||
type: string;
|
||||
}
|
||||
5
promo-ui2/src/api/menu/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import request from '@/axios'
|
||||
|
||||
export const getMenuListApi = () => {
|
||||
return request.get({ url: '/mock/menu/list' })
|
||||
}
|
||||
38
promo-ui2/src/api/request/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import request from '@/axios'
|
||||
import { RequestResponse } from './types'
|
||||
|
||||
export const request1 = () => {
|
||||
return request.get<IResponse<RequestResponse>>({
|
||||
url: '/mock/request/1'
|
||||
})
|
||||
}
|
||||
|
||||
export const request2 = () => {
|
||||
return request.get<IResponse<RequestResponse>>({
|
||||
url: '/mock/request/2'
|
||||
})
|
||||
}
|
||||
|
||||
export const request3 = () => {
|
||||
return request.get<IResponse<RequestResponse>>({
|
||||
url: '/mock/request/3'
|
||||
})
|
||||
}
|
||||
|
||||
export const request4 = () => {
|
||||
return request.get<IResponse<RequestResponse>>({
|
||||
url: '/mock/request/4'
|
||||
})
|
||||
}
|
||||
|
||||
export const request5 = () => {
|
||||
return request.get<IResponse<RequestResponse>>({
|
||||
url: '/mock/request/5'
|
||||
})
|
||||
}
|
||||
|
||||
export const expired = () => {
|
||||
return request.get<IResponse<RequestResponse>>({
|
||||
url: '/mock/request/expired'
|
||||
})
|
||||
}
|
||||
3
promo-ui2/src/api/request/types.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface RequestResponse {
|
||||
data: string
|
||||
}
|
||||
5
promo-ui2/src/api/role/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import request from '@/axios'
|
||||
|
||||
export const getRoleListApi = () => {
|
||||
return request.get({ url: '/mock/role/table' })
|
||||
}
|
||||
29
promo-ui2/src/api/subflow/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import request from '@/axios'
|
||||
import { getPurchaseByUserIdRequest, getSubBranchesRequest } from './types'
|
||||
|
||||
export function getSubBranches(req: getSubBranchesRequest) {
|
||||
return request.get({
|
||||
url: '/promo/turnover/getSubBranches',
|
||||
params: req
|
||||
})
|
||||
}
|
||||
|
||||
export function getPurchaseByUserId(req: getPurchaseByUserIdRequest) {
|
||||
return request.get({
|
||||
url: '/promo/turnover/getPurchaseByUserId/' + req.userId,
|
||||
params: req
|
||||
})
|
||||
}
|
||||
|
||||
export function getCommissionRateByUserId(userId) {
|
||||
return request.get({
|
||||
url: '/promo/turnover/getCommissionRateByUserId/' + userId
|
||||
})
|
||||
}
|
||||
|
||||
export function updateCommissionRate(data) {
|
||||
return request.put({
|
||||
url: '/promo/turnover/updateCommissionRate',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
14
promo-ui2/src/api/subflow/types.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export interface getPurchaseByUserIdRequest {
|
||||
pageNum: number
|
||||
pageSize: number
|
||||
beginTime: undefined | string
|
||||
endTime: string | undefined
|
||||
userId: undefined | string | number
|
||||
}
|
||||
|
||||
export interface getSubBranchesRequest {
|
||||
pageNum: number
|
||||
pageSize: number
|
||||
userId: undefined | string | number
|
||||
playerId: undefined | string | number
|
||||
}
|
||||
26
promo-ui2/src/api/table/index.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import request from '@/axios'
|
||||
import type { TableData } from './types'
|
||||
|
||||
export const getTableListApi = (params: any) => {
|
||||
return request.get({ url: '/mock/example/list', params })
|
||||
}
|
||||
|
||||
export const getCardTableListApi = (params: any) => {
|
||||
return request.get({ url: '/mock/card/list', params })
|
||||
}
|
||||
|
||||
export const getTreeTableListApi = (params: any) => {
|
||||
return request.get({ url: '/mock/example/treeList', params })
|
||||
}
|
||||
|
||||
export const saveTableApi = (data: Partial<TableData>): Promise<IResponse> => {
|
||||
return request.post({ url: '/mock/example/save', data })
|
||||
}
|
||||
|
||||
export const getTableDetApi = (id: string): Promise<IResponse<TableData>> => {
|
||||
return request.get({ url: '/mock/example/detail', params: { id } })
|
||||
}
|
||||
|
||||
export const delTableListApi = (ids: string[] | number[]): Promise<IResponse> => {
|
||||
return request.post({ url: '/mock/example/delete', data: { ids } })
|
||||
}
|
||||
9
promo-ui2/src/api/table/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export type TableData = {
|
||||
id: string
|
||||
author: string
|
||||
title: string
|
||||
content: string
|
||||
importance: number
|
||||
display_time: string
|
||||
pageviews: number
|
||||
}
|
||||
27
promo-ui2/src/api/turnover/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import request from '@/axios'
|
||||
|
||||
export function getRealTimeData() {
|
||||
return request.get({
|
||||
url: '/promo/turnover/getRealTimeData'
|
||||
})
|
||||
}
|
||||
|
||||
export function getLast10DaysPromotionData() {
|
||||
return request.get({
|
||||
url: '/promo/turnover/getLast10DaysPromotionData'
|
||||
})
|
||||
}
|
||||
|
||||
export function getAnchorPromotionData(query) {
|
||||
return request.get({
|
||||
url: '/promo/turnover/getAnchorPromotionData',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function queryPromoteWelfare(query) {
|
||||
return request.get({
|
||||
url: '/promo/turnover/queryPromoteWelfare',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
33
promo-ui2/src/api/user/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import request from '@/axios'
|
||||
|
||||
export function getInfo() {
|
||||
return request.get({
|
||||
url: '/api/getInfo',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function updateInvitationCode(data: any) {
|
||||
return request.post({
|
||||
url: '/promo/updateInvitationCode',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getShareDomain() {
|
||||
return request.get({
|
||||
url: '/promo/getsharedomain'
|
||||
})
|
||||
}
|
||||
|
||||
export function getRechargeListApi() {
|
||||
return request.get({
|
||||
url: '/promo/card/list'
|
||||
})
|
||||
}
|
||||
|
||||
export function generateCardListApi(id:number, num:number) {
|
||||
return request.post({
|
||||
url: `/promo/generateCard/${id}/${num}`
|
||||
})
|
||||
}
|
||||
BIN
promo-ui2/src/assets/imgs/avatar.jpg
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
promo-ui2/src/assets/imgs/logo.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
promo-ui2/src/assets/imgs/personal-center-bg.jpg
Normal file
|
After Width: | Height: | Size: 181 KiB |
1
promo-ui2/src/assets/svgs/403.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
promo-ui2/src/assets/svgs/404.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
1
promo-ui2/src/assets/svgs/500.svg
Normal file
|
After Width: | Height: | Size: 19 KiB |
1
promo-ui2/src/assets/svgs/icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.147.062a13 13 0 014.94.945c1.55.63 2.907 1.526 4.069 2.688a13.148 13.148 0 012.761 4.069c.678 1.55 1.017 3.245 1.017 5.086v102.3c0 3.681-1.187 6.733-3.56 9.155-2.373 2.422-5.352 3.633-8.937 3.633H12.992c-3.875 0-7-1.26-9.373-3.779-2.373-2.518-3.56-5.667-3.56-9.445V12.704c0-3.39 1.163-6.345 3.488-8.863C5.872 1.32 8.972.062 12.847.062h102.3zM81.434 109.047c1.744 0 3.003-.412 3.778-1.235.775-.824 1.163-1.914 1.163-3.27 0-1.26-.388-2.325-1.163-3.197-.775-.872-2.034-1.307-3.778-1.307H72.57c.097-.194.145-.485.145-.872V27.09h9.01c1.743 0 2.954-.436 3.633-1.308.678-.872 1.017-1.938 1.017-3.197 0-1.26-.34-2.325-1.017-3.197-.679-.872-1.89-1.308-3.633-1.308H46.268c-1.743 0-2.954.436-3.632 1.308-.678.872-1.018 1.938-1.018 3.197 0 1.26.34 2.325 1.018 3.197.678.872 1.889 1.308 3.632 1.308h8.138v72.075c0 .193.024.339.073.436.048.096.072.242.072.436H46.56c-1.744 0-3.003.435-3.778 1.307-.775.872-1.163 1.938-1.163 3.197 0 1.356.388 2.446 1.163 3.27.775.823 2.034 1.235 3.778 1.235h34.875z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
promo-ui2/src/assets/svgs/login-bg.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="5760" height="3040"><image width="5760" height="3040" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAFoAAAAvgAQMAAAC1QKagAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUsNEr///91v/yPAAAA AWJLR0QB/wIt3gAAAAd0SU1FB+YBBQYyN1c3BnEAAAhjSURBVHja7cExAQAAAMKg9U9tDB+gAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAACAtwFzzwABY3VrRQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMi0wMS0wNVQwNjo1 MDo1MyswMDowMCfNlVoAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjItMDEtMDVUMDY6NTA6NTQrMDA6 MDCTNxNoAAAAAElFTkSuQmCC"/></svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
1
promo-ui2/src/assets/svgs/login-box-bg.svg
Normal file
|
After Width: | Height: | Size: 33 KiB |
1
promo-ui2/src/assets/svgs/message.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg>
|
||||
|
After Width: | Height: | Size: 669 B |
1
promo-ui2/src/assets/svgs/money.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg>
|
||||
|
After Width: | Height: | Size: 335 B |
1
promo-ui2/src/assets/svgs/peoples.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg>
|
||||
|
After Width: | Height: | Size: 731 B |
1
promo-ui2/src/assets/svgs/shopping.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 013.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 01-3.889 2.843 10.582 10.582 0 01-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 01-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 01-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 013.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 013.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 01-3.89 2.843 11 11 0 01-4.732 1.066 10.58 10.58 0 01-4.667-1.066 12.478 12.478 0 01-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 01-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 013.824-2.772 11.212 11.212 0 014.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 01.778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 01-2.788 8.743 1236.373 1236.373 0 00-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 01-1.88-4.478 44.128 44.128 0 01-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 01-2.14-2.558 10.416 10.416 0 01-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 011.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
58
promo-ui2/src/axios/config.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { AxiosResponse, InternalAxiosRequestConfig } from './types'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import qs from 'qs'
|
||||
import { SUCCESS_CODE, TRANSFORM_REQUEST_DATA } from '@/constants'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import { objToFormData } from '@/utils'
|
||||
|
||||
const defaultRequestInterceptors = (config: InternalAxiosRequestConfig) => {
|
||||
if (
|
||||
config.method === 'post' &&
|
||||
config.headers['Content-Type'] === 'application/x-www-form-urlencoded'
|
||||
) {
|
||||
config.data = qs.stringify(config.data)
|
||||
} else if (
|
||||
TRANSFORM_REQUEST_DATA &&
|
||||
config.method === 'post' &&
|
||||
config.headers['Content-Type'] === 'multipart/form-data' &&
|
||||
!(config.data instanceof FormData)
|
||||
) {
|
||||
config.data = objToFormData(config.data)
|
||||
}
|
||||
if (config.method === 'get' && config.params) {
|
||||
let url = config.url as string
|
||||
url += '?'
|
||||
const keys = Object.keys(config.params)
|
||||
for (const key of keys) {
|
||||
if (config.params[key] !== void 0 && config.params[key] !== null) {
|
||||
url += `${key}=${encodeURIComponent(config.params[key])}&`
|
||||
}
|
||||
}
|
||||
url = url.substring(0, url.length - 1)
|
||||
config.params = {}
|
||||
config.url = url
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const defaultResponseInterceptors = (response: AxiosResponse) => {
|
||||
if (response?.config?.responseType === 'blob') {
|
||||
// 如果是文件流,直接过
|
||||
return response
|
||||
} else if (response.data.code === SUCCESS_CODE || response.data.code === 200) {
|
||||
return response.data
|
||||
} else {
|
||||
if (response?.data?.msg) {
|
||||
ElMessage.error(response?.data?.msg)
|
||||
} else {
|
||||
ElMessage.error(response?.data?.message)
|
||||
}
|
||||
|
||||
if (response?.data?.code === 401) {
|
||||
const userStore = useUserStoreWithOut()
|
||||
userStore.logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { defaultResponseInterceptors, defaultRequestInterceptors }
|
||||
42
promo-ui2/src/axios/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import service from './service'
|
||||
import { CONTENT_TYPE } from '@/constants'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
|
||||
const request = (option: AxiosConfig) => {
|
||||
const { url, method, params, data, headers, responseType } = option
|
||||
|
||||
const userStore = useUserStoreWithOut()
|
||||
return service.request({
|
||||
url: url,
|
||||
method,
|
||||
params,
|
||||
data: data,
|
||||
responseType: responseType,
|
||||
headers: {
|
||||
'Content-Type': CONTENT_TYPE,
|
||||
[userStore.getTokenKey ?? 'Authorization']: userStore.getToken ?? '',
|
||||
...headers
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
get: <T = any>(option: AxiosConfig) => {
|
||||
return request({ method: 'get', ...option }) as Promise<IResponse<T>>
|
||||
},
|
||||
post: <T = any>(option: AxiosConfig) => {
|
||||
return request({ method: 'post', ...option }) as Promise<IResponse<T>>
|
||||
},
|
||||
delete: <T = any>(option: AxiosConfig) => {
|
||||
return request({ method: 'delete', ...option }) as Promise<IResponse<T>>
|
||||
},
|
||||
put: <T = any>(option: AxiosConfig) => {
|
||||
return request({ method: 'put', ...option }) as Promise<IResponse<T>>
|
||||
},
|
||||
cancelRequest: (url: string | string[]) => {
|
||||
return service.cancelRequest(url)
|
||||
},
|
||||
cancelAllRequest: () => {
|
||||
return service.cancelAllRequest()
|
||||
}
|
||||
}
|
||||
76
promo-ui2/src/axios/service.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import axios, { AxiosError } from 'axios'
|
||||
import { defaultRequestInterceptors, defaultResponseInterceptors } from './config'
|
||||
import { AxiosInstance, InternalAxiosRequestConfig, RequestConfig, AxiosResponse } from './types'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { REQUEST_TIMEOUT } from '@/constants'
|
||||
|
||||
export const PATH_URL = import.meta.env.VITE_API_BASE_PATH
|
||||
|
||||
const abortControllerMap: Map<string, AbortController> = new Map()
|
||||
|
||||
const axiosInstance: AxiosInstance = axios.create({
|
||||
timeout: REQUEST_TIMEOUT,
|
||||
baseURL: PATH_URL
|
||||
})
|
||||
|
||||
axiosInstance.interceptors.request.use((res: InternalAxiosRequestConfig) => {
|
||||
const controller = new AbortController()
|
||||
const url = res.url || ''
|
||||
res.signal = controller.signal
|
||||
abortControllerMap.set(
|
||||
import.meta.env.VITE_USE_MOCK === 'true' ? url.replace('/mock', '') : url,
|
||||
controller
|
||||
)
|
||||
return res
|
||||
})
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
(res: AxiosResponse) => {
|
||||
const url = res.config.url || ''
|
||||
abortControllerMap.delete(url)
|
||||
// 这里不能做任何处理,否则后面的 interceptors 拿不到完整的上下文了
|
||||
return res
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
console.log('err: ' + error) // for debug
|
||||
ElMessage.error(error.message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
axiosInstance.interceptors.request.use(defaultRequestInterceptors)
|
||||
axiosInstance.interceptors.response.use(defaultResponseInterceptors)
|
||||
|
||||
const service = {
|
||||
request: (config: RequestConfig) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (config.interceptors?.requestInterceptors) {
|
||||
config = config.interceptors.requestInterceptors(config as any)
|
||||
}
|
||||
|
||||
axiosInstance
|
||||
.request(config)
|
||||
.then((res) => {
|
||||
resolve(res)
|
||||
})
|
||||
.catch((err: any) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
cancelRequest: (url: string | string[]) => {
|
||||
const urlList = Array.isArray(url) ? url : [url]
|
||||
for (const _url of urlList) {
|
||||
abortControllerMap.get(_url)?.abort()
|
||||
abortControllerMap.delete(_url)
|
||||
}
|
||||
},
|
||||
cancelAllRequest() {
|
||||
for (const [_, controller] of abortControllerMap) {
|
||||
controller.abort()
|
||||
}
|
||||
abortControllerMap.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export default service
|
||||
31
promo-ui2/src/axios/types/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type {
|
||||
InternalAxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
AxiosRequestConfig,
|
||||
AxiosInstance,
|
||||
AxiosRequestHeaders,
|
||||
AxiosError
|
||||
} from 'axios'
|
||||
|
||||
interface RequestInterceptors<T> {
|
||||
// 请求拦截
|
||||
requestInterceptors?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig
|
||||
requestInterceptorsCatch?: (err: any) => any
|
||||
// 响应拦截
|
||||
responseInterceptors?: (config: T) => T
|
||||
responseInterceptorsCatch?: (err: any) => any
|
||||
}
|
||||
|
||||
interface RequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
|
||||
interceptors?: RequestInterceptors<T>
|
||||
}
|
||||
|
||||
export {
|
||||
AxiosResponse,
|
||||
RequestInterceptors,
|
||||
RequestConfig,
|
||||
AxiosInstance,
|
||||
InternalAxiosRequestConfig,
|
||||
AxiosRequestHeaders,
|
||||
AxiosError
|
||||
}
|
||||
4
promo-ui2/src/components/Avatars/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import Avatars from './src/Avatars.vue'
|
||||
|
||||
export type { AvatarItem } from './src/types'
|
||||
export { Avatars }
|
||||
79
promo-ui2/src/components/Avatars/src/Avatars.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import { ComponentSize, ElAvatar, ElTooltip } from 'element-plus'
|
||||
import { PropType, computed } from 'vue'
|
||||
import { AvatarItem } from './types'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('avatars')
|
||||
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: [String, Number] as PropType<ComponentSize | number>,
|
||||
default: ''
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
data: {
|
||||
type: Array as PropType<AvatarItem[]>,
|
||||
default: () => []
|
||||
},
|
||||
showTooltip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const filterData = computed(() => props.data.slice(0, props.max))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefixCls" class="flex items-center">
|
||||
<template v-for="item in filterData" :key="item.url">
|
||||
<template v-if="showTooltip && item.name">
|
||||
<ElTooltip :content="item.name" placement="top">
|
||||
<ElAvatar
|
||||
:size="size"
|
||||
:src="item.url"
|
||||
class="relative"
|
||||
:style="{
|
||||
zIndex: filterData.indexOf(item)
|
||||
}"
|
||||
/>
|
||||
</ElTooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElAvatar
|
||||
:size="size"
|
||||
:src="item.url"
|
||||
class="relative"
|
||||
:style="{
|
||||
zIndex: filterData.indexOf(item)
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<ElAvatar
|
||||
v-if="data.length > max"
|
||||
:style="{
|
||||
zIndex: data.length
|
||||
}"
|
||||
>
|
||||
<span>+{{ data.length - max }}</span>
|
||||
</ElAvatar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
@prefix-cls: ~'@{adminNamespace}-avatars';
|
||||
|
||||
.@{prefix-cls} {
|
||||
.@{elNamespace}-avatar + .@{elNamespace}-avatar {
|
||||
margin-left: -15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
4
promo-ui2/src/components/Avatars/src/types/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface AvatarItem {
|
||||
url: string
|
||||
name?: string
|
||||
}
|
||||
3
promo-ui2/src/components/Backtop/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Backtop from './src/Backtop.vue'
|
||||
|
||||
export { Backtop }
|
||||
15
promo-ui2/src/components/Backtop/src/Backtop.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { ElBacktop } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls, variables } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('backtop')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElBacktop
|
||||
:class="prefixCls"
|
||||
:target="`.${variables.namespace}-layout-content-scrollbar .${variables.elNamespace}-scrollbar__wrap`"
|
||||
/>
|
||||
</template>
|
||||
3
promo-ui2/src/components/Breadcrumb/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Breadcrumb from './src/Breadcrumb.vue'
|
||||
|
||||
export { Breadcrumb }
|
||||
126
promo-ui2/src/components/Breadcrumb/src/Breadcrumb.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<script lang="tsx">
|
||||
import { ElBreadcrumb, ElBreadcrumbItem } from 'element-plus'
|
||||
import { ref, watch, computed, unref, defineComponent, TransitionGroup } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import { filterBreadcrumb } from './helper'
|
||||
import { filter, treeToList } from '@/utils/tree'
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('breadcrumb')
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
// 面包屑图标
|
||||
const breadcrumbIcon = computed(() => appStore.getBreadcrumbIcon)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Breadcrumb',
|
||||
setup() {
|
||||
const { currentRoute } = useRouter()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const levelList = ref<AppRouteRecordRaw[]>([])
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const menuRouters = computed(() => {
|
||||
const routers = permissionStore.getRouters
|
||||
return filterBreadcrumb(routers)
|
||||
})
|
||||
|
||||
const getBreadcrumb = () => {
|
||||
const currentPath = currentRoute.value.matched.slice(-1)[0].path
|
||||
levelList.value = filter<AppRouteRecordRaw>(unref(menuRouters), (node: AppRouteRecordRaw) => {
|
||||
return node.path === currentPath
|
||||
})
|
||||
}
|
||||
|
||||
const renderBreadcrumb = () => {
|
||||
const breadcrumbList = treeToList<AppRouteRecordRaw[]>(unref(levelList))
|
||||
return breadcrumbList.map((v) => {
|
||||
const disabled = !v.redirect || v.redirect === 'noredirect'
|
||||
const meta = v.meta
|
||||
return (
|
||||
<ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}>
|
||||
{meta?.icon && breadcrumbIcon.value ? (
|
||||
<>
|
||||
<Icon icon={meta.icon} class="mr-[5px]"></Icon> {t(v?.meta?.title || '')}
|
||||
</>
|
||||
) : (
|
||||
t(v?.meta?.title || '')
|
||||
)}
|
||||
</ElBreadcrumbItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => currentRoute.value,
|
||||
(route: RouteLocationNormalizedLoaded) => {
|
||||
if (route.path.startsWith('/redirect/')) {
|
||||
return
|
||||
}
|
||||
getBreadcrumb()
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
return () => (
|
||||
<ElBreadcrumb separator="/" class={`${prefixCls} flex items-center h-full ml-[10px]`}>
|
||||
<TransitionGroup appear enter-active-class="animate__animated animate__fadeInRight">
|
||||
{renderBreadcrumb()}
|
||||
</TransitionGroup>
|
||||
</ElBreadcrumb>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{elNamespace}-breadcrumb';
|
||||
|
||||
.@{prefix-cls} {
|
||||
:deep(&__item) {
|
||||
display: flex;
|
||||
.@{prefix-cls}__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--top-header-text-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(&__item):not(:last-child) {
|
||||
.@{prefix-cls}__inner {
|
||||
color: var(--top-header-text-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(&__item):last-child {
|
||||
.@{prefix-cls}__inner {
|
||||
color: var(--el-text-color-placeholder);
|
||||
|
||||
&:hover {
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
30
promo-ui2/src/components/Breadcrumb/src/helper.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { pathResolve } from '@/utils/routerHelper'
|
||||
|
||||
export const filterBreadcrumb = (
|
||||
routes: AppRouteRecordRaw[],
|
||||
parentPath = ''
|
||||
): AppRouteRecordRaw[] => {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
const meta = route?.meta
|
||||
if (meta.hidden && !meta.canTo) {
|
||||
continue
|
||||
}
|
||||
|
||||
const data: AppRouteRecordRaw =
|
||||
!meta.alwaysShow && route.children?.length === 1
|
||||
? { ...route.children[0], path: pathResolve(route.path, route.children[0].path) }
|
||||
: { ...route }
|
||||
|
||||
data.path = pathResolve(parentPath, data.path)
|
||||
|
||||
if (data.children) {
|
||||
data.children = filterBreadcrumb(data.children, data.path)
|
||||
}
|
||||
if (data) {
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
3
promo-ui2/src/components/Button/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import BaseButton from './src/Button.vue'
|
||||
|
||||
export { BaseButton }
|
||||
121
promo-ui2/src/components/Button/src/Button.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { ElButton, ComponentSize, ButtonType } from 'element-plus'
|
||||
import { PropType, Component, computed, unref } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const getTheme = computed(() => appStore.getTheme)
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('button')
|
||||
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: String as PropType<ComponentSize>,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<ButtonType>,
|
||||
default: 'default'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
plain: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
text: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bg: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
link: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
circle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
loadingIcon: {
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
default: undefined
|
||||
},
|
||||
icon: {
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
default: undefined
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
nativeType: {
|
||||
type: String as PropType<'button' | 'submit' | 'reset'>,
|
||||
default: 'button'
|
||||
},
|
||||
autoInsertSpace: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
darker: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tag: {
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
default: 'button'
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['click'])
|
||||
|
||||
const color = computed(() => {
|
||||
const { type, link } = props
|
||||
if (type === 'primary' && !link) {
|
||||
return unref(getTheme).elColorPrimary
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const style = computed(() => {
|
||||
const { type, link } = props
|
||||
if (type === 'primary' && !link) {
|
||||
return '--el-button-text-color: #fff; --el-button-hover-text-color: #fff'
|
||||
}
|
||||
return ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElButton
|
||||
:class="`${prefixCls} color-#fff`"
|
||||
v-bind="{ ...props }"
|
||||
:color="color"
|
||||
:style="style"
|
||||
@click="() => emits('click')"
|
||||
>
|
||||
<slot></slot>
|
||||
<slot name="icon"></slot>
|
||||
<slot name="loading"></slot>
|
||||
</ElButton>
|
||||
</template>
|
||||
3
promo-ui2/src/components/CodeEditor/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import CodeEditor from './src/CodeEditor.vue'
|
||||
|
||||
export { CodeEditor }
|
||||
119
promo-ui2/src/components/CodeEditor/src/CodeEditor.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script setup lang="tsx">
|
||||
import { useMonacoEditor } from '@/hooks/web/useMonacoEditor'
|
||||
import { onMounted, computed, watch, ref } from 'vue'
|
||||
import { ElSelect, ElOption, ElFormItem, ElForm } from 'element-plus'
|
||||
import { languageOptions, themeOptions } from './config/config'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
languageSelector?: boolean
|
||||
language?: string
|
||||
themeSelector?: boolean
|
||||
theme?: string
|
||||
editorOption?: object
|
||||
modelValue: string
|
||||
}>(),
|
||||
{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
languageSelector: true,
|
||||
language: 'javascript',
|
||||
themeSelector: true,
|
||||
theme: 'vs-dark',
|
||||
editorOption: () => ({}),
|
||||
modelValue: ''
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'blur'): void
|
||||
(e: 'update:modelValue', val: string): void
|
||||
}>()
|
||||
|
||||
const monacoEditorStyle = computed(() => {
|
||||
return {
|
||||
width: typeof props.width === 'string' ? props.width : props.width + 'px',
|
||||
height: typeof props.height === 'string' ? props.height : props.height + 'px'
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
monacoEditorRef,
|
||||
createEditor,
|
||||
updateVal,
|
||||
updateOptions,
|
||||
getEditor,
|
||||
changeLanguage,
|
||||
changeTheme
|
||||
} = useMonacoEditor(props.language)
|
||||
|
||||
onMounted(() => {
|
||||
const monacoEditor = createEditor(props.editorOption)
|
||||
updateMonacoVal(props.modelValue)
|
||||
monacoEditor?.onDidChangeModelContent(() => {
|
||||
emits('update:modelValue', monacoEditor!.getValue())
|
||||
})
|
||||
monacoEditor?.onDidBlurEditorText(() => {
|
||||
emits('blur')
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
updateMonacoVal(props.modelValue)
|
||||
}
|
||||
)
|
||||
|
||||
const localLanguage = ref(props.language)
|
||||
|
||||
watch(localLanguage, (newLanguage) => {
|
||||
changeLanguage(newLanguage)
|
||||
})
|
||||
|
||||
const localTheme = ref(props.theme)
|
||||
watch(localTheme, (newTheme) => {
|
||||
changeTheme(newTheme)
|
||||
})
|
||||
|
||||
function updateMonacoVal(val: string) {
|
||||
if (val !== getEditor()?.getValue()) {
|
||||
updateVal(val)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ updateOptions })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElForm :inline="true">
|
||||
<ElFormItem v-if="languageSelector" label="language" class="w-30% mb-5px!">
|
||||
<ElSelect
|
||||
v-model="localLanguage"
|
||||
placeholder="Please select language"
|
||||
size="small"
|
||||
filterable
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in languageOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem v-if="themeSelector" label="theme" class="w-30% mb-5px!">
|
||||
<ElSelect v-model="localTheme" placeholder="Please select language" size="small" filterable>
|
||||
<ElOption
|
||||
v-for="item in themeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<div ref="monacoEditorRef" :style="monacoEditorStyle"></div>
|
||||
</template>
|
||||
129
promo-ui2/src/components/CodeEditor/src/config/config.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
export const languageOptions = [
|
||||
{ label: 'plaintext', value: 'plaintext' },
|
||||
{ label: 'abap', value: 'abap' },
|
||||
{ label: 'apex', value: 'apex' },
|
||||
{ label: 'azcli', value: 'azcli' },
|
||||
{ label: 'bat', value: 'bat' },
|
||||
{ label: 'bicep', value: 'bicep' },
|
||||
{ label: 'cameligo', value: 'cameligo' },
|
||||
{ label: 'clojure', value: 'clojure' },
|
||||
{ label: 'coffeescript', value: 'coffeescript' },
|
||||
{ label: 'c', value: 'c' },
|
||||
{ label: 'cpp', value: 'cpp' },
|
||||
{ label: 'csharp', value: 'csharp' },
|
||||
{ label: 'csp', value: 'csp' },
|
||||
{ label: 'css', value: 'css' },
|
||||
{ label: 'cypher', value: 'cypher' },
|
||||
{ label: 'dart', value: 'dart' },
|
||||
{ label: 'dockerfile', value: 'dockerfile' },
|
||||
{ label: 'ecl', value: 'ecl' },
|
||||
{ label: 'elixir', value: 'elixir' },
|
||||
{ label: 'flow9', value: 'flow9' },
|
||||
{ label: 'fsharp', value: 'fsharp' },
|
||||
{ label: 'freemarker2', value: 'freemarker2' },
|
||||
{
|
||||
label: 'freemarker2.tag-angle.interpolation-dollar',
|
||||
value: 'freemarker2.tag-angle.interpolation-dollar'
|
||||
},
|
||||
{
|
||||
label: 'freemarker2.tag-bracket.interpolation-dollar',
|
||||
value: 'freemarker2.tag-bracket.interpolation-dollar'
|
||||
},
|
||||
{
|
||||
label: 'freemarker2.tag-angle.interpolation-bracket',
|
||||
value: 'freemarker2.tag-angle.interpolation-bracket'
|
||||
},
|
||||
{
|
||||
label: 'freemarker2.tag-bracket.interpolation-bracket',
|
||||
value: 'freemarker2.tag-bracket.interpolation-bracket'
|
||||
},
|
||||
{
|
||||
label: 'freemarker2.tag-auto.interpolation-dollar',
|
||||
value: 'freemarker2.tag-auto.interpolation-dollar'
|
||||
},
|
||||
{
|
||||
label: 'freemarker2.tag-auto.interpolation-bracket',
|
||||
value: 'freemarker2.tag-auto.interpolation-bracket'
|
||||
},
|
||||
{ label: 'go', value: 'go' },
|
||||
{ label: 'graphql', value: 'graphql' },
|
||||
{ label: 'handlebars', value: 'handlebars' },
|
||||
{ label: 'hcl', value: 'hcl' },
|
||||
{ label: 'html', value: 'html' },
|
||||
{ label: 'ini', value: 'ini' },
|
||||
{ label: 'java', value: 'java' },
|
||||
{ label: 'javascript', value: 'javascript' },
|
||||
{ label: 'julia', value: 'julia' },
|
||||
{ label: 'kotlin', value: 'kotlin' },
|
||||
{ label: 'less', value: 'less' },
|
||||
{ label: 'lexon', value: 'lexon' },
|
||||
{ label: 'lua', value: 'lua' },
|
||||
{ label: 'liquid', value: 'liquid' },
|
||||
{ label: 'm3', value: 'm3' },
|
||||
{ label: 'markdown', value: 'markdown' },
|
||||
{ label: 'mdx', value: 'mdx' },
|
||||
{ label: 'mips', value: 'mips' },
|
||||
{ label: 'msdax', value: 'msdax' },
|
||||
{ label: 'mysql', value: 'mysql' },
|
||||
{ label: 'objective-c', value: 'objective-c' },
|
||||
{ label: 'pascal', value: 'pascal' },
|
||||
{ label: 'pascaligo', value: 'pascaligo' },
|
||||
{ label: 'perl', value: 'perl' },
|
||||
{ label: 'pgsql', value: 'pgsql' },
|
||||
{ label: 'php', value: 'php' },
|
||||
{ label: 'pla', value: 'pla' },
|
||||
{ label: 'postiats', value: 'postiats' },
|
||||
{ label: 'powerquery', value: 'powerquery' },
|
||||
{ label: 'powershell', value: 'powershell' },
|
||||
{ label: 'proto', value: 'proto' },
|
||||
{ label: 'pug', value: 'pug' },
|
||||
{ label: 'python', value: 'python' },
|
||||
{ label: 'qsharp', value: 'qsharp' },
|
||||
{ label: 'r', value: 'r' },
|
||||
{ label: 'razor', value: 'razor' },
|
||||
{ label: 'redis', value: 'redis' },
|
||||
{ label: 'redshift', value: 'redshift' },
|
||||
{ label: 'restructuredtext', value: 'restructuredtext' },
|
||||
{ label: 'ruby', value: 'ruby' },
|
||||
{ label: 'rust', value: 'rust' },
|
||||
{ label: 'sb', value: 'sb' },
|
||||
{ label: 'scala', value: 'scala' },
|
||||
{ label: 'scheme', value: 'scheme' },
|
||||
{ label: 'scss', value: 'scss' },
|
||||
{ label: 'shell', value: 'shell' },
|
||||
{ label: 'sol', value: 'sol' },
|
||||
{ label: 'aes', value: 'aes' },
|
||||
{ label: 'sparql', value: 'sparql' },
|
||||
{ label: 'sql', value: 'sql' },
|
||||
{ label: 'st', value: 'st' },
|
||||
{ label: 'swift', value: 'swift' },
|
||||
{ label: 'systemverilog', value: 'systemverilog' },
|
||||
{ label: 'verilog', value: 'verilog' },
|
||||
{ label: 'tcl', value: 'tcl' },
|
||||
{ label: 'twig', value: 'twig' },
|
||||
{ label: 'typescript', value: 'typescript' },
|
||||
{ label: 'vb', value: 'vb' },
|
||||
{ label: 'wgsl', value: 'wgsl' },
|
||||
{ label: 'xml', value: 'xml' },
|
||||
{ label: 'yaml', value: 'yaml' },
|
||||
{ label: 'json', value: 'json' }
|
||||
]
|
||||
|
||||
export const themeOptions = [
|
||||
{
|
||||
label: 'vs',
|
||||
value: 'vs'
|
||||
},
|
||||
{
|
||||
label: 'vs-dark',
|
||||
value: 'vs-dark'
|
||||
},
|
||||
{
|
||||
label: 'hc-black',
|
||||
value: 'hc-black'
|
||||
},
|
||||
{
|
||||
label: 'hc-light',
|
||||
value: 'hc-light'
|
||||
}
|
||||
]
|
||||
3
promo-ui2/src/components/Collapse/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Collapse from './src/Collapse.vue'
|
||||
|
||||
export { Collapse }
|
||||
34
promo-ui2/src/components/Collapse/src/Collapse.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, unref } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('collapse')
|
||||
|
||||
defineProps({
|
||||
color: propTypes.string.def('')
|
||||
})
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const collapse = computed(() => appStore.getCollapse)
|
||||
|
||||
const toggleCollapse = () => {
|
||||
const collapsed = unref(collapse)
|
||||
appStore.setCollapse(!collapsed)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefixCls" @click="toggleCollapse">
|
||||
<Icon
|
||||
:size="18"
|
||||
:icon="collapse ? 'vi-ant-design:menu-unfold-outlined' : 'vi-ant-design:menu-fold-outlined'"
|
||||
:color="color"
|
||||
class="cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
5
promo-ui2/src/components/ConfigGlobal/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import ConfigGlobal from './src/ConfigGlobal.vue'
|
||||
|
||||
export type { ConfigGlobalTypes } from './src/types'
|
||||
|
||||
export { ConfigGlobal }
|
||||
62
promo-ui2/src/components/ConfigGlobal/src/ConfigGlobal.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { provide, computed, watch, onMounted } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ComponentSize, ElConfigProvider } from 'element-plus'
|
||||
import { useLocaleStore } from '@/store/modules/locale'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { setCssVar } from '@/utils'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { variables } = useDesign()
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const props = defineProps({
|
||||
size: propTypes.oneOf<ComponentSize>(['default', 'small', 'large']).def('default')
|
||||
})
|
||||
|
||||
provide('configGlobal', props)
|
||||
|
||||
// 初始化所有主题色
|
||||
onMounted(() => {
|
||||
appStore.setCssVarTheme()
|
||||
})
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
// 监听窗口变化
|
||||
watch(
|
||||
() => width.value,
|
||||
(width: number) => {
|
||||
if (width < 768) {
|
||||
!appStore.getMobile ? appStore.setMobile(true) : undefined
|
||||
setCssVar('--left-menu-min-width', '0')
|
||||
appStore.setCollapse(true)
|
||||
appStore.getLayout !== 'classic' ? appStore.setLayout('classic') : undefined
|
||||
} else {
|
||||
appStore.getMobile ? appStore.setMobile(false) : undefined
|
||||
setCssVar('--left-menu-min-width', '64px')
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
// 多语言相关
|
||||
const localeStore = useLocaleStore()
|
||||
|
||||
const currentLocale = computed(() => localeStore.currentLocale)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElConfigProvider
|
||||
:namespace="variables.elNamespace"
|
||||
:locale="currentLocale.elLocale"
|
||||
:message="{ max: 1 }"
|
||||
:size="size"
|
||||
>
|
||||
<slot></slot>
|
||||
</ElConfigProvider>
|
||||
</template>
|
||||
5
promo-ui2/src/components/ConfigGlobal/src/types/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { ComponentSize } from 'element-plus'
|
||||
|
||||
export interface ConfigGlobalTypes {
|
||||
size?: ComponentSize
|
||||
}
|
||||
3
promo-ui2/src/components/ContentDetailWrap/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import ContentDetailWrap from './src/ContentDetailWrap.vue'
|
||||
|
||||
export { ContentDetailWrap }
|
||||
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { ElCard } from 'element-plus'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('content-detail-wrap')
|
||||
|
||||
defineProps({
|
||||
title: propTypes.string.def(''),
|
||||
message: propTypes.string.def('')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="[`${prefixCls}-container`, 'relative']">
|
||||
<ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never">
|
||||
<div class="mb-20px pb-20px" style="border-bottom: 1px solid var(--el-border-color)">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</ElCard>
|
||||
</div>
|
||||
</template>
|
||||
3
promo-ui2/src/components/ContentWrap/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import ContentWrap from './src/ContentWrap.vue'
|
||||
|
||||
export { ContentWrap }
|
||||
36
promo-ui2/src/components/ContentWrap/src/ContentWrap.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { ElCard, ElTooltip } from 'element-plus'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('content-wrap')
|
||||
|
||||
defineProps({
|
||||
title: propTypes.string.def(''),
|
||||
message: propTypes.string.def('')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard :class="[prefixCls]" shadow="never">
|
||||
<template v-if="title" #header>
|
||||
<div class="flex items-center">
|
||||
<span class="text-16px font-700">{{ title }}</span>
|
||||
<ElTooltip v-if="message" effect="dark" placement="right">
|
||||
<template #content>
|
||||
<div class="max-w-200px">{{ message }}</div>
|
||||
</template>
|
||||
<Icon class="ml-5px" icon="vi-bi:question-circle-fill" :size="14" />
|
||||
</ElTooltip>
|
||||
<div class="flex pl-20px flex-grow">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</ElCard>
|
||||
</template>
|
||||
12
promo-ui2/src/components/ContextMenu/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import ContextMenu from './src/ContextMenu.vue'
|
||||
import { ElDropdown } from 'element-plus'
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
|
||||
export type { ContextMenuSchema } from './src/types'
|
||||
|
||||
export interface ContextMenuExpose {
|
||||
elDropdownMenuRef: ComponentRef<typeof ElDropdown>
|
||||
tagItem: RouteLocationNormalizedLoaded
|
||||
}
|
||||
|
||||
export { ContextMenu }
|
||||
72
promo-ui2/src/components/ContextMenu/src/ContextMenu.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<script setup lang="ts">
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus'
|
||||
import { PropType, ref } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import { ContextMenuSchema } from './types'
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('context-menu')
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const emit = defineEmits(['visibleChange'])
|
||||
|
||||
const props = defineProps({
|
||||
schema: {
|
||||
type: Array as PropType<ContextMenuSchema[]>,
|
||||
default: () => []
|
||||
},
|
||||
trigger: {
|
||||
type: String as PropType<'click' | 'hover' | 'focus' | 'contextmenu'>,
|
||||
default: 'contextmenu'
|
||||
},
|
||||
tagItem: {
|
||||
type: Object as PropType<RouteLocationNormalizedLoaded>,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const command = (item: ContextMenuSchema) => {
|
||||
item.command && item.command(item)
|
||||
}
|
||||
|
||||
const visibleChange = (visible: boolean) => {
|
||||
emit('visibleChange', visible, props.tagItem)
|
||||
}
|
||||
|
||||
const elDropdownMenuRef = ref<ComponentRef<typeof ElDropdown>>()
|
||||
|
||||
defineExpose({
|
||||
elDropdownMenuRef,
|
||||
tagItem: props.tagItem
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDropdown
|
||||
ref="elDropdownMenuRef"
|
||||
:class="prefixCls"
|
||||
:trigger="trigger"
|
||||
placement="bottom-start"
|
||||
@command="command"
|
||||
@visible-change="visibleChange"
|
||||
popper-class="v-context-menu-popper"
|
||||
>
|
||||
<slot></slot>
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem
|
||||
v-for="(item, index) in schema"
|
||||
:key="`dropdown${index}`"
|
||||
:divided="item.divided"
|
||||
:disabled="item.disabled"
|
||||
:command="item"
|
||||
>
|
||||
<Icon :icon="item.icon" /> {{ t(item.label) }}
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
</ElDropdown>
|
||||
</template>
|
||||
7
promo-ui2/src/components/ContextMenu/src/types/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface ContextMenuSchema {
|
||||
disabled?: boolean
|
||||
divided?: boolean
|
||||
icon?: string
|
||||
label: string
|
||||
command?: (item: ContextMenuSchema) => void
|
||||
}
|
||||
3
promo-ui2/src/components/CountTo/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import CountTo from './src/CountTo.vue'
|
||||
|
||||
export { CountTo }
|
||||
180
promo-ui2/src/components/CountTo/src/CountTo.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed, watch, onMounted, unref, toRef, PropType } from 'vue'
|
||||
import { isNumber } from '@/utils/is'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('count-to')
|
||||
|
||||
const props = defineProps({
|
||||
startVal: propTypes.number.def(0),
|
||||
endVal: propTypes.number.def(2021),
|
||||
duration: propTypes.number.def(3000),
|
||||
autoplay: propTypes.bool.def(true),
|
||||
decimals: propTypes.number.validate((value: number) => value >= 0).def(0),
|
||||
decimal: propTypes.string.def('.'),
|
||||
separator: propTypes.string.def(','),
|
||||
prefix: propTypes.string.def(''),
|
||||
suffix: propTypes.string.def(''),
|
||||
useEasing: propTypes.bool.def(true),
|
||||
easingFn: {
|
||||
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
|
||||
default(t: number, b: number, c: number, d: number) {
|
||||
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['mounted', 'callback'])
|
||||
|
||||
const formatNumber = (num: number | string) => {
|
||||
const { decimals, decimal, separator, suffix, prefix } = props
|
||||
num = Number(num).toFixed(decimals)
|
||||
num += ''
|
||||
const x = num.split('.')
|
||||
let x1 = x[0]
|
||||
const x2 = x.length > 1 ? decimal + x[1] : ''
|
||||
const rgx = /(\d+)(\d{3})/
|
||||
if (separator && !isNumber(separator)) {
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + separator + '$2')
|
||||
}
|
||||
}
|
||||
return prefix + x1 + x2 + suffix
|
||||
}
|
||||
|
||||
const state = reactive<{
|
||||
localStartVal: number
|
||||
printVal: number | null
|
||||
displayValue: string
|
||||
paused: boolean
|
||||
localDuration: number | null
|
||||
startTime: number | null
|
||||
timestamp: number | null
|
||||
rAF: any
|
||||
remaining: number | null
|
||||
}>({
|
||||
localStartVal: props.startVal,
|
||||
displayValue: formatNumber(props.startVal),
|
||||
printVal: null,
|
||||
paused: false,
|
||||
localDuration: props.duration,
|
||||
startTime: null,
|
||||
timestamp: null,
|
||||
remaining: null,
|
||||
rAF: null
|
||||
})
|
||||
|
||||
const displayValue = toRef(state, 'displayValue')
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autoplay) {
|
||||
start()
|
||||
}
|
||||
emit('mounted')
|
||||
})
|
||||
|
||||
const getCountDown = computed(() => {
|
||||
return props.startVal > props.endVal
|
||||
})
|
||||
|
||||
watch([() => props.startVal, () => props.endVal], () => {
|
||||
if (props.autoplay) {
|
||||
start()
|
||||
}
|
||||
})
|
||||
|
||||
const start = () => {
|
||||
const { startVal, duration } = props
|
||||
state.localStartVal = startVal
|
||||
state.startTime = null
|
||||
state.localDuration = duration
|
||||
state.paused = false
|
||||
state.rAF = requestAnimationFrame(count)
|
||||
}
|
||||
|
||||
const pauseResume = () => {
|
||||
if (state.paused) {
|
||||
resume()
|
||||
state.paused = false
|
||||
} else {
|
||||
pause()
|
||||
state.paused = true
|
||||
}
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
cancelAnimationFrame(state.rAF)
|
||||
}
|
||||
|
||||
const resume = () => {
|
||||
state.startTime = null
|
||||
state.localDuration = +(state.remaining as number)
|
||||
state.localStartVal = +(state.printVal as number)
|
||||
requestAnimationFrame(count)
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
state.startTime = null
|
||||
cancelAnimationFrame(state.rAF)
|
||||
state.displayValue = formatNumber(props.startVal)
|
||||
}
|
||||
|
||||
const count = (timestamp: number) => {
|
||||
const { useEasing, easingFn, endVal } = props
|
||||
if (!state.startTime) state.startTime = timestamp
|
||||
state.timestamp = timestamp
|
||||
const progress = timestamp - state.startTime
|
||||
state.remaining = (state.localDuration as number) - progress
|
||||
if (useEasing) {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number)
|
||||
} else {
|
||||
state.printVal = easingFn(
|
||||
progress,
|
||||
state.localStartVal,
|
||||
endVal - state.localStartVal,
|
||||
state.localDuration as number
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
(state.localStartVal - endVal) * (progress / (state.localDuration as number))
|
||||
} else {
|
||||
state.printVal =
|
||||
state.localStartVal +
|
||||
(endVal - state.localStartVal) * (progress / (state.localDuration as number))
|
||||
}
|
||||
}
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal = state.printVal < endVal ? endVal : state.printVal
|
||||
} else {
|
||||
state.printVal = state.printVal > endVal ? endVal : state.printVal
|
||||
}
|
||||
state.displayValue = formatNumber(state.printVal!)
|
||||
if (progress < (state.localDuration as number)) {
|
||||
state.rAF = requestAnimationFrame(count)
|
||||
} else {
|
||||
emit('callback')
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
pauseResume,
|
||||
reset,
|
||||
start,
|
||||
pause
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="prefixCls">
|
||||
{{ displayValue }}
|
||||
</span>
|
||||
</template>
|
||||
5
promo-ui2/src/components/Descriptions/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import Descriptions from './src/Descriptions.vue'
|
||||
|
||||
export type { DescriptionsSchema } from './src/types'
|
||||
|
||||
export { Descriptions }
|
||||
197
promo-ui2/src/components/Descriptions/src/Descriptions.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<script lang="tsx">
|
||||
import { ElCollapseTransition, ElTooltip, ElRow, ElCol } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ref, unref, PropType, computed, defineComponent } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { DescriptionsSchema } from './types'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { get } from 'lodash-es'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const mobile = computed(() => appStore.getMobile)
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('descriptions')
|
||||
|
||||
const defaultData = '-'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Descriptions',
|
||||
props: {
|
||||
title: propTypes.string.def(''),
|
||||
message: propTypes.string.def(''),
|
||||
collapse: propTypes.bool.def(true),
|
||||
border: propTypes.bool.def(true),
|
||||
column: propTypes.number.def(2),
|
||||
size: propTypes.oneOf(['large', 'default', 'small']).def('default'),
|
||||
direction: propTypes.oneOf(['horizontal', 'vertical']).def('horizontal'),
|
||||
extra: propTypes.string.def(''),
|
||||
schema: {
|
||||
type: Array as PropType<DescriptionsSchema[]>,
|
||||
default: () => []
|
||||
},
|
||||
data: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
setup(props, { attrs }) {
|
||||
const getBindValue = computed((): any => {
|
||||
const delArr: string[] = ['title', 'message', 'collapse', 'schema', 'data', 'class']
|
||||
const obj = { ...attrs, ...props }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
if (unref(mobile)) {
|
||||
obj.direction = 'vertical'
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
const getBindItemValue = (item: DescriptionsSchema) => {
|
||||
const delArr: string[] = ['field']
|
||||
const obj = { ...item }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return {
|
||||
labelClassName: `${prefixCls}-label`,
|
||||
...obj
|
||||
}
|
||||
}
|
||||
|
||||
// 折叠
|
||||
const show = ref(true)
|
||||
|
||||
const toggleClick = () => {
|
||||
if (props.collapse) {
|
||||
show.value = !unref(show)
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
prefixCls,
|
||||
'bg-[var(--el-color-white)] dark:bg-[var(--el-bg-color)] dark:border-[var(--el-border-color)] dark:border-1px'
|
||||
]}
|
||||
>
|
||||
{props.title ? (
|
||||
<div
|
||||
class={[
|
||||
`${prefixCls}-header`,
|
||||
'relative h-50px flex justify-between items-center layout-border__bottom px-10px cursor-pointer'
|
||||
]}
|
||||
onClick={toggleClick}
|
||||
>
|
||||
<div class={[`${prefixCls}-header__title`, 'relative font-18px font-bold ml-10px']}>
|
||||
<div class="flex items-center">
|
||||
{props.title}
|
||||
{props.message ? (
|
||||
<ElTooltip content={props.message} placement="right">
|
||||
<Icon icon="vi-bi:question-circle-fill" class="ml-5px" size={14} />
|
||||
</ElTooltip>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{props.collapse ? (
|
||||
<Icon icon={show.value ? 'vi-ep:arrow-down' : 'vi-ep:arrow-up'} />
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<ElCollapseTransition>
|
||||
<div v-show={unref(show)} class={[`${prefixCls}-content`, 'p-20px']}>
|
||||
<ElRow
|
||||
gutter={0}
|
||||
{...unref(getBindValue)}
|
||||
class="outline-1px outline-[var(--el-border-color-lighter)] outline-solid"
|
||||
>
|
||||
{props.schema.map((item) => {
|
||||
return (
|
||||
<ElCol
|
||||
key={item.field}
|
||||
span={item.span || 24 / props.column}
|
||||
class="flex items-stretch"
|
||||
>
|
||||
{props.direction === 'horizontal' ? (
|
||||
<div class="flex items-stretch bg-[var(--el-fill-color-light)] outline-1px outline-[var(--el-border-color-lighter)] outline-solid flex-1">
|
||||
<div
|
||||
{...getBindItemValue(item)}
|
||||
class="w-120px text-left px-8px py-11px font-700 color-[var(--el-text-color-regular)] border-r-1px border-r-[var(--el-border-color-lighter)] border-r-solid "
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
<div class="flex-1 px-8px py-11px bg-[var(--el-bg-color)] color-[var(--el-text-color-primary)] text-size-14px">
|
||||
{item.slots?.default
|
||||
? item.slots?.default(props.data)
|
||||
: (get(props.data, item.field) ?? defaultData)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div class="bg-[var(--el-fill-color-light)] outline-1px outline-[var(--el-border-color-lighter)] outline-solid flex-1">
|
||||
<div
|
||||
{...getBindItemValue(item)}
|
||||
class="text-left px-8px py-11px font-700 color-[var(--el-text-color-regular)] border-b-1px border-b-[var(--el-border-color-lighter)] border-b-solid"
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
<div class="flex-1 px-8px py-11px bg-[var(--el-bg-color)] color-[var(--el-text-color-primary)] text-size-14px">
|
||||
{item.slots?.default
|
||||
? item.slots?.default(props.data)
|
||||
: (get(props.data, item.field) ?? defaultData)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ElCol>
|
||||
)
|
||||
})}
|
||||
</ElRow>
|
||||
</div>
|
||||
</ElCollapseTransition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{adminNamespace}-descriptions';
|
||||
|
||||
:deep(.@{elNamespace}-descriptions__header) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.@{prefix-cls}-header {
|
||||
&__title {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: -10px;
|
||||
width: 4px;
|
||||
height: 70%;
|
||||
background: var(--el-color-primary);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.@{prefix-cls}-label) {
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
// .@{prefix-cls}-content {
|
||||
// :deep(.@{elNamespace}-descriptions__cell) {
|
||||
// width: 0;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
15
promo-ui2/src/components/Descriptions/src/types/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export interface DescriptionsSchema {
|
||||
span?: number // 占多少分
|
||||
field: string // 字段名
|
||||
label?: string // label名
|
||||
width?: string | number
|
||||
minWidth?: string | number
|
||||
align?: 'left' | 'center' | 'right'
|
||||
labelAlign?: 'left' | 'center' | 'right'
|
||||
className?: string
|
||||
labelClassName?: string
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
label?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
}
|
||||
178
promo-ui2/src/components/Dialog/hooks/useResize.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useResize = (props?: {
|
||||
minHeightPx?: number
|
||||
minWidthPx?: number
|
||||
initHeight?: number
|
||||
initWidth?: number
|
||||
}) => {
|
||||
const {
|
||||
minHeightPx = 400,
|
||||
minWidthPx = window.innerWidth / 2,
|
||||
initHeight = 400,
|
||||
initWidth = window.innerWidth / 2
|
||||
} = props || {}
|
||||
// 屏幕宽度的 50% 作为最小宽度
|
||||
// const minWidthPx = window.innerWidth / 2
|
||||
// 固定的最小高度 400px
|
||||
// const minHeightPx = 400
|
||||
// 初始高度限制为 400px
|
||||
const maxHeight = ref(initHeight + 'px')
|
||||
// 初始宽度限制为 50%
|
||||
const minWidth = ref(initWidth + 'px')
|
||||
const setupDrag = (elDialog: any, el: any) => {
|
||||
// 获取对话框元素
|
||||
// 是否正在调整大小的标志
|
||||
let isResizing = false
|
||||
// 当前调整的方向
|
||||
let currentResizeDirection = ''
|
||||
|
||||
// 鼠标移动时的事件处理器,用于检测鼠标位置并设置相应的光标样式
|
||||
const handleMouseMove = (e: any) => {
|
||||
const rect = elDialog.getBoundingClientRect()
|
||||
// 鼠标相对于对话框左侧的偏移量
|
||||
const offsetX = e.clientX - rect.left
|
||||
// 鼠标相对于对话框顶部的偏移量
|
||||
const offsetY = e.clientY - rect.top
|
||||
const width = elDialog.clientWidth
|
||||
const height = elDialog.clientHeight
|
||||
|
||||
// 获取对话框的内边距
|
||||
const computedStyle = window.getComputedStyle(elDialog)
|
||||
const paddingLeft = parseFloat(computedStyle.paddingLeft)
|
||||
const paddingRight = parseFloat(computedStyle.paddingRight)
|
||||
const paddingBottom = parseFloat(computedStyle.paddingBottom)
|
||||
const paddingTop = parseFloat(computedStyle.paddingTop)
|
||||
|
||||
// 根据鼠标位置设置相应的光标样式和调整方向
|
||||
if (!isResizing) {
|
||||
if (offsetX < paddingLeft && offsetY > paddingTop && offsetY < height - paddingBottom) {
|
||||
elDialog.style.cursor = 'ew-resize' // 左右箭头
|
||||
currentResizeDirection = 'left'
|
||||
} else if (
|
||||
offsetX > width - paddingRight &&
|
||||
offsetY > paddingTop &&
|
||||
offsetY < height - paddingBottom
|
||||
) {
|
||||
elDialog.style.cursor = 'ew-resize' // 左右箭头
|
||||
currentResizeDirection = 'right'
|
||||
} else if (
|
||||
offsetY < paddingTop &&
|
||||
offsetX > paddingLeft &&
|
||||
offsetX < width - paddingRight
|
||||
) {
|
||||
elDialog.style.cursor = 'ns-resize' // 上下箭头
|
||||
currentResizeDirection = 'top'
|
||||
} else if (
|
||||
offsetY > height - paddingBottom &&
|
||||
offsetX > paddingLeft &&
|
||||
offsetX < width - paddingRight
|
||||
) {
|
||||
elDialog.style.cursor = 'ns-resize' // 上下箭头
|
||||
currentResizeDirection = 'bottom'
|
||||
} else if (offsetX < paddingLeft && offsetY < paddingTop) {
|
||||
elDialog.style.cursor = 'nwse-resize' // 左上右下箭头
|
||||
currentResizeDirection = 'top-left'
|
||||
} else if (offsetX > width - paddingRight && offsetY < paddingTop) {
|
||||
elDialog.style.cursor = 'nesw-resize' // 右上左下箭头
|
||||
currentResizeDirection = 'top-right'
|
||||
} else if (offsetX < paddingLeft && offsetY > height - paddingBottom) {
|
||||
elDialog.style.cursor = 'nesw-resize' // 右上左下箭头
|
||||
currentResizeDirection = 'bottom-left'
|
||||
} else if (offsetX > width - paddingRight && offsetY > height - paddingBottom) {
|
||||
elDialog.style.cursor = 'nwse-resize' // 左上右下箭头
|
||||
currentResizeDirection = 'bottom-right'
|
||||
} else {
|
||||
elDialog.style.cursor = 'default'
|
||||
currentResizeDirection = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标按下时的事件处理器,开始调整对话框大小
|
||||
const handleMouseDown = (e) => {
|
||||
if (currentResizeDirection) {
|
||||
isResizing = true
|
||||
|
||||
const initialX = e.clientX
|
||||
const initialY = e.clientY
|
||||
const initialWidth = elDialog.clientWidth
|
||||
const initialHeight = el.querySelector('.el-dialog__body').clientHeight
|
||||
|
||||
// 调整大小的事件处理器
|
||||
const handleResizing = (e: any) => {
|
||||
if (!isResizing) return
|
||||
|
||||
let newWidth = initialWidth
|
||||
let newHeight = initialHeight
|
||||
|
||||
// 根据当前调整方向计算新的宽度和高度
|
||||
if (currentResizeDirection.includes('right')) {
|
||||
newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection.includes('left')) {
|
||||
newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection.includes('bottom')) {
|
||||
newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection.includes('top')) {
|
||||
newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'top-left') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'top-right') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'bottom-left') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
|
||||
if (currentResizeDirection === 'bottom-right') {
|
||||
newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
|
||||
minWidth.value = `${newWidth}px`
|
||||
newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
|
||||
maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
|
||||
}
|
||||
}
|
||||
// 停止调整大小的事件处理器
|
||||
const stopResizing = () => {
|
||||
isResizing = false
|
||||
document.removeEventListener('mousemove', handleResizing)
|
||||
document.removeEventListener('mouseup', stopResizing)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleResizing)
|
||||
document.addEventListener('mouseup', stopResizing)
|
||||
}
|
||||
}
|
||||
elDialog.addEventListener('mousemove', handleMouseMove)
|
||||
elDialog.addEventListener('mousedown', handleMouseDown)
|
||||
}
|
||||
|
||||
return {
|
||||
setupDrag,
|
||||
maxHeight,
|
||||
minWidth
|
||||
}
|
||||
}
|
||||
3
promo-ui2/src/components/Dialog/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Dialog from './src/Dialog.vue'
|
||||
|
||||
export { Dialog }
|
||||
145
promo-ui2/src/components/Dialog/src/Dialog.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<script setup lang="ts">
|
||||
import { ElDialog, ElScrollbar } from 'element-plus'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { computed, useAttrs, ref, unref, useSlots, watch, nextTick } from 'vue'
|
||||
import { isNumber } from '@/utils/is'
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.bool.def(false),
|
||||
title: propTypes.string.def('Dialog'),
|
||||
fullscreen: propTypes.bool.def(true),
|
||||
maxHeight: propTypes.oneOfType([String, Number]).def('400px')
|
||||
})
|
||||
|
||||
const getBindValue = computed(() => {
|
||||
const delArr: string[] = ['fullscreen', 'title', 'maxHeight']
|
||||
const attrs = useAttrs()
|
||||
const obj = { ...attrs, ...props }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
const isFullscreen = ref(false)
|
||||
|
||||
const toggleFull = () => {
|
||||
isFullscreen.value = !unref(isFullscreen)
|
||||
}
|
||||
|
||||
const dialogHeight = ref(isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight)
|
||||
|
||||
watch(
|
||||
() => isFullscreen.value,
|
||||
async (val: boolean) => {
|
||||
await nextTick()
|
||||
if (val) {
|
||||
const windowHeight = document.documentElement.offsetHeight
|
||||
dialogHeight.value = `${windowHeight - 55 - 60 - (slots.footer ? 63 : 0)}px`
|
||||
} else {
|
||||
dialogHeight.value = isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.maxHeight,
|
||||
(val) => {
|
||||
dialogHeight.value = isNumber(val) ? `${val}px` : val
|
||||
}
|
||||
)
|
||||
|
||||
const dialogStyle = computed(() => {
|
||||
return {
|
||||
height: unref(dialogHeight)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDialog
|
||||
v-bind="getBindValue"
|
||||
:fullscreen="isFullscreen"
|
||||
destroy-on-close
|
||||
lock-scroll
|
||||
draggable
|
||||
top="0"
|
||||
:close-on-click-modal="false"
|
||||
:show-close="false"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<div class="flex justify-between items-center h-54px pl-15px pr-15px relative">
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
<div
|
||||
class="h-54px flex justify-between items-center absolute top-[50%] right-15px translate-y-[-50%]"
|
||||
>
|
||||
<Icon
|
||||
v-if="fullscreen"
|
||||
class="cursor-pointer is-hover !h-54px mr-10px"
|
||||
:icon="
|
||||
isFullscreen ? 'vi-radix-icons:exit-full-screen' : 'vi-radix-icons:enter-full-screen'
|
||||
"
|
||||
color="var(--el-color-info)"
|
||||
hover-color="var(--el-color-primary)"
|
||||
@click="toggleFull"
|
||||
/>
|
||||
<Icon
|
||||
class="cursor-pointer is-hover !h-54px"
|
||||
icon="vi-ep:close"
|
||||
hover-color="var(--el-color-primary)"
|
||||
color="var(--el-color-info)"
|
||||
@click="close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElScrollbar :style="dialogStyle">
|
||||
<slot></slot>
|
||||
</ElScrollbar>
|
||||
|
||||
<template v-if="slots.footer" #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.@{elNamespace}-overlay-dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.@{elNamespace}-dialog {
|
||||
margin: 0 !important;
|
||||
|
||||
&__header {
|
||||
height: 54px;
|
||||
padding: 0;
|
||||
margin-right: 0 !important;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 15px !important;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
&__headerbtn {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
73
promo-ui2/src/components/Dialog/src/ResizeDialog.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<script lang="tsx" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { computed, getCurrentInstance, onMounted, unref, useAttrs, useSlots } from 'vue'
|
||||
import Dialog from './Dialog.vue'
|
||||
import { useResize } from '../hooks/useResize'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.bool.def(false),
|
||||
title: propTypes.string.def('Dialog'),
|
||||
fullscreen: propTypes.bool.def(true),
|
||||
initWidth: propTypes.number.def(window.innerWidth / 2),
|
||||
initHeight: propTypes.number.def(200),
|
||||
minResizeWidth: propTypes.number.def(window.innerWidth / 2),
|
||||
minResizeHeight: propTypes.number.def(200)
|
||||
})
|
||||
const { maxHeight, minWidth, setupDrag } = useResize({
|
||||
minHeightPx: props.minResizeHeight,
|
||||
minWidthPx: props.minResizeWidth,
|
||||
initHeight: props.initHeight,
|
||||
initWidth: props.initWidth
|
||||
})
|
||||
|
||||
const vResize = {
|
||||
mounted(el) {
|
||||
const observer = new MutationObserver(() => {
|
||||
const elDialog = el.querySelector('.el-dialog')
|
||||
|
||||
if (elDialog) {
|
||||
// 在确认 `elDialog` 已渲染后进行处理
|
||||
setupDrag(elDialog, el)
|
||||
// observer.disconnect() // 一旦获取到元素,停止观察
|
||||
}
|
||||
})
|
||||
// 开始观察子节点的变化
|
||||
observer.observe(el, { childList: true, subtree: true })
|
||||
}
|
||||
}
|
||||
|
||||
const attrs = useAttrs()
|
||||
const slots = useSlots()
|
||||
const getBindValue = computed(() => {
|
||||
const delArr: string[] = ['maxHeight', 'width']
|
||||
const obj = Object.assign({}, { ...unref(attrs), ...props })
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
const instance = getCurrentInstance()
|
||||
const initDirective = () => {
|
||||
const directives = instance?.appContext?.app._context?.directives
|
||||
|
||||
// 检查指令是否已经注册
|
||||
if (!directives || !directives['resize']) {
|
||||
instance?.appContext?.app.directive('resize', vResize)
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
initDirective()
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div v-resize>
|
||||
<Dialog v-bind="getBindValue" :maxHeight="maxHeight" :width="minWidth">
|
||||
<slot></slot>
|
||||
<template v-if="slots.footer" #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
3
promo-ui2/src/components/Echart/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Echart from './src/Echart.vue'
|
||||
|
||||
export { Echart }
|
||||
115
promo-ui2/src/components/Echart/src/Echart.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import echarts from '@/plugins/echarts'
|
||||
import { debounce } from 'lodash-es'
|
||||
import 'echarts-wordcloud'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { computed, PropType, ref, unref, watch, onMounted, onBeforeUnmount, onActivated } from 'vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { isString } from '@/utils/is'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls, variables } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('echart')
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Object as PropType<EChartsOption>,
|
||||
required: true
|
||||
},
|
||||
width: propTypes.oneOfType([Number, String]).def('100%'),
|
||||
height: propTypes.oneOfType([Number, String]).def('500px')
|
||||
})
|
||||
|
||||
const isDark = computed(() => appStore.getIsDark)
|
||||
|
||||
const theme = computed(() => {
|
||||
const echartTheme: boolean | string = unref(isDark) ? true : 'auto'
|
||||
|
||||
return echartTheme
|
||||
})
|
||||
|
||||
const options = computed(() => {
|
||||
return Object.assign(props.options, {
|
||||
darkMode: unref(theme)
|
||||
})
|
||||
})
|
||||
|
||||
const elRef = ref<ElRef>()
|
||||
|
||||
let echartRef: Nullable<echarts.ECharts> = null
|
||||
|
||||
const contentEl = ref<Element>()
|
||||
|
||||
const styles = computed(() => {
|
||||
const width = isString(props.width) ? props.width : `${props.width}px`
|
||||
const height = isString(props.height) ? props.height : `${props.height}px`
|
||||
|
||||
return {
|
||||
width,
|
||||
height
|
||||
}
|
||||
})
|
||||
|
||||
const initChart = () => {
|
||||
if (unref(elRef) && props.options) {
|
||||
echartRef = echarts.init(unref(elRef) as HTMLElement)
|
||||
echartRef?.setOption(unref(options))
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => options.value,
|
||||
(options) => {
|
||||
if (echartRef) {
|
||||
echartRef?.setOption(options)
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
const resizeHandler = debounce(() => {
|
||||
if (echartRef) {
|
||||
echartRef.resize()
|
||||
}
|
||||
}, 100)
|
||||
|
||||
const contentResizeHandler = async (e: TransitionEvent) => {
|
||||
if (e.propertyName === 'width') {
|
||||
resizeHandler()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
initChart()
|
||||
}, 0)
|
||||
|
||||
window.addEventListener('resize', resizeHandler)
|
||||
|
||||
contentEl.value = document.getElementsByClassName(`${variables.namespace}-layout-content`)[0]
|
||||
unref(contentEl) &&
|
||||
(unref(contentEl) as Element).addEventListener('transitionend', contentResizeHandler)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', resizeHandler)
|
||||
unref(contentEl) &&
|
||||
(unref(contentEl) as Element).removeEventListener('transitionend', contentResizeHandler)
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
if (echartRef) {
|
||||
echartRef.resize()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="elRef" :class="[$attrs.class, prefixCls]" :style="styles"></div>
|
||||
</template>
|
||||
8
promo-ui2/src/components/Editor/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import Editor from './src/Editor.vue'
|
||||
import { IDomEditor } from '@wangeditor/editor'
|
||||
|
||||
export interface EditorExpose {
|
||||
getEditorRef: () => Promise<IDomEditor>
|
||||
}
|
||||
|
||||
export { Editor }
|
||||
135
promo-ui2/src/components/Editor/src/Editor.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, computed, PropType, unref, nextTick, ref, watch, shallowRef } from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { IDomEditor, IEditorConfig, i18nChangeLanguage } from '@wangeditor/editor'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { isNumber } from '@/utils/is'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useLocaleStore } from '@/store/modules/locale'
|
||||
|
||||
const localeStore = useLocaleStore()
|
||||
|
||||
const currentLocale = computed(() => localeStore.getCurrentLocale)
|
||||
|
||||
i18nChangeLanguage(unref(currentLocale).lang)
|
||||
|
||||
const props = defineProps({
|
||||
editorId: propTypes.string.def('wangeEditor-1'),
|
||||
height: propTypes.oneOfType([Number, String]).def('500px'),
|
||||
editorConfig: {
|
||||
type: Object as PropType<IEditorConfig>,
|
||||
default: () => undefined
|
||||
},
|
||||
modelValue: propTypes.string.def('')
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef<IDomEditor>()
|
||||
|
||||
const valueHtml = ref('')
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string) => {
|
||||
if (val === unref(valueHtml)) return
|
||||
valueHtml.value = val
|
||||
}
|
||||
)
|
||||
|
||||
// 监听
|
||||
watch(
|
||||
() => valueHtml.value,
|
||||
(val: string) => {
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
)
|
||||
|
||||
const handleCreated = (editor: IDomEditor) => {
|
||||
editorRef.value = editor
|
||||
valueHtml.value = props.modelValue
|
||||
}
|
||||
|
||||
// 编辑器配置
|
||||
const editorConfig = computed((): IEditorConfig => {
|
||||
return Object.assign(
|
||||
{
|
||||
readOnly: false,
|
||||
customAlert: (s: string, t: string) => {
|
||||
switch (t) {
|
||||
case 'success':
|
||||
ElMessage.success(s)
|
||||
break
|
||||
case 'info':
|
||||
ElMessage.info(s)
|
||||
break
|
||||
case 'warning':
|
||||
ElMessage.warning(s)
|
||||
break
|
||||
case 'error':
|
||||
ElMessage.error(s)
|
||||
break
|
||||
default:
|
||||
ElMessage.info(s)
|
||||
break
|
||||
}
|
||||
},
|
||||
autoFocus: false,
|
||||
scroll: true,
|
||||
uploadImgShowBase64: true
|
||||
},
|
||||
props.editorConfig || {}
|
||||
)
|
||||
})
|
||||
|
||||
const editorStyle = computed(() => {
|
||||
return {
|
||||
height: isNumber(props.height) ? `${props.height}px` : props.height
|
||||
}
|
||||
})
|
||||
|
||||
// 回调函数
|
||||
const handleChange = (editor: IDomEditor) => {
|
||||
emit('change', editor)
|
||||
}
|
||||
|
||||
// 组件销毁时,及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = unref(editorRef.value)
|
||||
|
||||
// 销毁,并移除 editor
|
||||
editor?.destroy()
|
||||
})
|
||||
|
||||
const getEditorRef = async (): Promise<IDomEditor> => {
|
||||
await nextTick()
|
||||
return unref(editorRef.value) as IDomEditor
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getEditorRef
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-1 border-solid border-[var(--el-border-color)] z-10">
|
||||
<!-- 工具栏 -->
|
||||
<Toolbar
|
||||
:editor="editorRef"
|
||||
:editorId="editorId"
|
||||
class="border-0 b-b-1 border-solid border-[var(--el-border-color)]"
|
||||
/>
|
||||
<!-- 编辑器 -->
|
||||
<Editor
|
||||
v-model="valueHtml"
|
||||
:editorId="editorId"
|
||||
:defaultConfig="editorConfig"
|
||||
:style="editorStyle"
|
||||
@on-change="handleChange"
|
||||
@on-created="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style src="@wangeditor/editor/dist/css/style.css"></style>
|
||||
3
promo-ui2/src/components/Error/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Error from './src/Error.vue'
|
||||
|
||||
export { Error }
|
||||
57
promo-ui2/src/components/Error/src/Error.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import pageError from '@/assets/svgs/404.svg'
|
||||
import networkError from '@/assets/svgs/500.svg'
|
||||
import noPermission from '@/assets/svgs/403.svg'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
interface ErrorMap {
|
||||
url: string
|
||||
message: string
|
||||
buttonText: string
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const errorMap: {
|
||||
[key: string]: ErrorMap
|
||||
} = {
|
||||
'404': {
|
||||
url: pageError,
|
||||
message: t('error.pageError'),
|
||||
buttonText: t('error.returnToHome')
|
||||
},
|
||||
'500': {
|
||||
url: networkError,
|
||||
message: t('error.networkError'),
|
||||
buttonText: t('error.returnToHome')
|
||||
},
|
||||
'403': {
|
||||
url: noPermission,
|
||||
message: t('error.noPermission'),
|
||||
buttonText: t('error.returnToHome')
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
type: propTypes.string.validate((v: string) => ['404', '500', '403'].includes(v)).def('404')
|
||||
})
|
||||
|
||||
const emit = defineEmits(['errorClick'])
|
||||
|
||||
const btnClick = () => {
|
||||
emit('errorClick', props.type)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-center">
|
||||
<div class="text-center">
|
||||
<img width="350" :src="errorMap[type].url" alt="" />
|
||||
<div class="text-14px text-[var(--el-color-info)]">{{ errorMap[type].message }}</div>
|
||||
<div class="mt-20px">
|
||||
<BaseButton type="primary" @click="btnClick">{{ errorMap[type].buttonText }}</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
3
promo-ui2/src/components/Footer/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Footer from './src/Footer.vue'
|
||||
|
||||
export { Footer }
|
||||
22
promo-ui2/src/components/Footer/src/Footer.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { computed } from 'vue'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('footer')
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const title = computed(() => appStore.getTitle)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="text-center text-[var(--el-text-color-placeholder)] bg-[var(--app-content-bg-color)] h-[var(--app-footer-height)] leading-[var(--app-footer-height)] dark:bg-[var(--el-bg-color)] overflow-hidden"
|
||||
>
|
||||
Copyright ©2021-present {{ title }}
|
||||
</div>
|
||||
</template>
|
||||
48
promo-ui2/src/components/Form/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import Form from './src/Form.vue'
|
||||
import type { FormSchema, FormSetProps } from './src/types'
|
||||
export type {
|
||||
ComponentNameEnum,
|
||||
ComponentName,
|
||||
InputComponentProps,
|
||||
AutocompleteComponentProps,
|
||||
InputNumberComponentProps,
|
||||
SelectOption,
|
||||
SelectComponentProps,
|
||||
SelectV2ComponentProps,
|
||||
CascaderComponentProps,
|
||||
SwitchComponentProps,
|
||||
RateComponentProps,
|
||||
ColorPickerComponentProps,
|
||||
TransferComponentProps,
|
||||
RadioOption,
|
||||
RadioGroupComponentProps,
|
||||
RadioButtonComponentProps,
|
||||
CheckboxOption,
|
||||
CheckboxGroupComponentProps,
|
||||
DividerComponentProps,
|
||||
DatePickerComponentProps,
|
||||
DateTimePickerComponentProps,
|
||||
TimePickerComponentProps,
|
||||
TimeSelectComponentProps,
|
||||
ColProps,
|
||||
FormSetProps,
|
||||
FormItemProps,
|
||||
FormSchema,
|
||||
FormProps,
|
||||
PlaceholderModel,
|
||||
InputPasswordComponentProps,
|
||||
TreeSelectComponentProps
|
||||
} from './src/types'
|
||||
|
||||
export interface FormExpose {
|
||||
setValues: (data: Recordable) => void
|
||||
setProps: (props: Recordable) => void
|
||||
delSchema: (field: string) => void
|
||||
addSchema: (formSchema: FormSchema, index?: number) => void
|
||||
setSchema: (schemaProps: FormSetProps[]) => void
|
||||
formModel: Recordable
|
||||
getComponentExpose: (field: string) => any
|
||||
getFormItemExpose: (field: string) => any
|
||||
}
|
||||
|
||||
export { Form }
|
||||
443
promo-ui2/src/components/Form/src/Form.vue
Normal file
@@ -0,0 +1,443 @@
|
||||
<script lang="tsx">
|
||||
import { PropType, defineComponent, ref, computed, unref, watch, onMounted } from 'vue'
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElRow,
|
||||
ElCol,
|
||||
FormRules,
|
||||
ComponentSize
|
||||
// FormItemProp
|
||||
} from 'element-plus'
|
||||
import { componentMap } from './helper/componentMap'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { getSlot } from '@/utils/tsxHelper'
|
||||
import {
|
||||
setTextPlaceholder,
|
||||
setGridProp,
|
||||
setComponentProps,
|
||||
setItemComponentSlots,
|
||||
initModel
|
||||
} from './helper'
|
||||
import { useRenderSelect } from './components/useRenderSelect'
|
||||
import { useRenderRadio } from './components/useRenderRadio'
|
||||
import { useRenderCheckbox } from './components/useRenderCheckbox'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { findIndex } from '@/utils'
|
||||
import { get, set } from 'lodash-es'
|
||||
import { FormProps } from './types'
|
||||
import {
|
||||
FormSchema,
|
||||
FormSetProps,
|
||||
ComponentNameEnum,
|
||||
SelectComponentProps,
|
||||
RadioGroupComponentProps,
|
||||
CheckboxGroupComponentProps
|
||||
} from './types'
|
||||
|
||||
const { renderSelectOptions } = useRenderSelect()
|
||||
const { renderRadioOptions } = useRenderRadio()
|
||||
const { renderCheckboxOptions } = useRenderCheckbox()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('form')
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Form',
|
||||
props: {
|
||||
// 生成Form的布局结构数组
|
||||
schema: {
|
||||
type: Array as PropType<FormSchema[]>,
|
||||
default: () => []
|
||||
},
|
||||
// 是否需要栅格布局
|
||||
isCol: propTypes.bool.def(true),
|
||||
// 表单数据对象
|
||||
model: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => ({})
|
||||
},
|
||||
// 是否自动设置placeholder
|
||||
autoSetPlaceholder: propTypes.bool.def(true),
|
||||
// 是否自定义内容
|
||||
isCustom: propTypes.bool.def(false),
|
||||
// 表单label宽度
|
||||
labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
|
||||
rules: {
|
||||
type: Object as PropType<FormRules>,
|
||||
default: () => ({})
|
||||
},
|
||||
labelPosition: propTypes.oneOf(['left', 'right', 'top']).def('right'),
|
||||
labelSuffix: propTypes.string.def(''),
|
||||
hideRequiredAsterisk: propTypes.bool.def(false),
|
||||
requireAsteriskPosition: propTypes.oneOf(['left', 'right']).def('left'),
|
||||
showMessage: propTypes.bool.def(true),
|
||||
inlineMessage: propTypes.bool.def(false),
|
||||
statusIcon: propTypes.bool.def(false),
|
||||
validateOnRuleChange: propTypes.bool.def(true),
|
||||
size: {
|
||||
type: String as PropType<ComponentSize>,
|
||||
default: undefined
|
||||
},
|
||||
disabled: propTypes.bool.def(false),
|
||||
scrollToError: propTypes.bool.def(false),
|
||||
scrollToErrorOffset: propTypes.oneOfType([Boolean, Object]).def(undefined)
|
||||
// onValidate: {
|
||||
// type: Function as PropType<(prop: FormItemProp, isValid: boolean, message: string) => void>,
|
||||
// default: () => {}
|
||||
// }
|
||||
},
|
||||
emits: ['register'],
|
||||
setup(props, { slots, expose, emit }) {
|
||||
// element form 实例
|
||||
const elFormRef = ref<ComponentRef<typeof ElForm>>()
|
||||
|
||||
const mergeProps = ref<FormProps>({})
|
||||
|
||||
const getProps = computed(() => {
|
||||
const propsObj = { ...props }
|
||||
Object.assign(propsObj, unref(mergeProps))
|
||||
return propsObj
|
||||
})
|
||||
|
||||
// 存储表单实例
|
||||
const formComponents = ref({})
|
||||
|
||||
// 存储form-item实例
|
||||
const formItemComponents = ref({})
|
||||
|
||||
// 表单数据
|
||||
const formModel = ref<Recordable>(props.model)
|
||||
|
||||
onMounted(() => {
|
||||
emit('register', unref(elFormRef)?.$parent, unref(elFormRef))
|
||||
})
|
||||
|
||||
// 对表单赋值
|
||||
const setValues = (data: Recordable = {}) => {
|
||||
formModel.value = Object.assign(unref(formModel), data)
|
||||
}
|
||||
|
||||
const setProps = (props: FormProps = {}) => {
|
||||
mergeProps.value = Object.assign(unref(mergeProps), props)
|
||||
}
|
||||
|
||||
const delSchema = (field: string) => {
|
||||
const { schema } = unref(getProps)
|
||||
|
||||
const index = findIndex(schema, (v: FormSchema) => v.field === field)
|
||||
if (index > -1) {
|
||||
schema.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const addSchema = (formSchema: FormSchema, index?: number) => {
|
||||
const { schema } = unref(getProps)
|
||||
if (index !== void 0) {
|
||||
schema.splice(index, 0, formSchema)
|
||||
return
|
||||
}
|
||||
schema.push(formSchema)
|
||||
}
|
||||
|
||||
const setSchema = (schemaProps: FormSetProps[]) => {
|
||||
const { schema } = unref(getProps)
|
||||
for (const v of schema) {
|
||||
for (const item of schemaProps) {
|
||||
if (v.field === item.field) {
|
||||
set(v, item.path, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getOptions = async (fn: Function, item: FormSchema) => {
|
||||
const options = await fn()
|
||||
if (!options || options.length == 0) return
|
||||
setSchema([
|
||||
{
|
||||
field: item.field,
|
||||
path:
|
||||
item.component === ComponentNameEnum.TREE_SELECT ||
|
||||
item.component === ComponentNameEnum.TRANSFER
|
||||
? 'componentProps.data'
|
||||
: 'componentProps.options',
|
||||
value: options
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取表单组件实例
|
||||
* @param filed 表单字段
|
||||
*/
|
||||
const getComponentExpose = (filed: string) => {
|
||||
return unref(formComponents)[filed]
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 获取formItem实例
|
||||
* @param filed 表单字段
|
||||
*/
|
||||
const getFormItemExpose = (filed: string) => {
|
||||
return unref(formItemComponents)[filed]
|
||||
}
|
||||
|
||||
const setComponentRefMap = (ref: any, filed: string) => {
|
||||
formComponents.value[filed] = ref
|
||||
}
|
||||
|
||||
const setFormItemRefMap = (ref: any, filed: string) => {
|
||||
formItemComponents.value[filed] = ref
|
||||
}
|
||||
|
||||
expose({
|
||||
setValues,
|
||||
formModel,
|
||||
setProps,
|
||||
delSchema,
|
||||
addSchema,
|
||||
setSchema,
|
||||
getComponentExpose,
|
||||
getFormItemExpose
|
||||
})
|
||||
|
||||
// 监听表单结构化数组,重新生成formModel
|
||||
watch(
|
||||
() => unref(getProps).schema,
|
||||
(schema = []) => {
|
||||
formModel.value = initModel(schema, unref(formModel))
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
// 渲染包裹标签,是否使用栅格布局
|
||||
const renderWrap = () => {
|
||||
const { isCol } = unref(getProps)
|
||||
const content = isCol ? (
|
||||
<ElRow gutter={20}>{renderFormItemWrap()}</ElRow>
|
||||
) : (
|
||||
renderFormItemWrap()
|
||||
)
|
||||
return content
|
||||
}
|
||||
|
||||
// 是否要渲染el-col
|
||||
const renderFormItemWrap = () => {
|
||||
// hidden属性表示隐藏,不做渲染
|
||||
const { schema = [], isCol } = unref(getProps)
|
||||
|
||||
return schema
|
||||
.filter((v) => !v.remove)
|
||||
.map((item) => {
|
||||
// 如果是 Divider 组件,需要自己占用一行
|
||||
const isDivider = item.component === 'Divider'
|
||||
const Com = componentMap['Divider'] as ReturnType<typeof defineComponent>
|
||||
return isDivider ? (
|
||||
<Com {...{ contentPosition: 'left', ...item.componentProps }}>{item?.label}</Com>
|
||||
) : isCol ? (
|
||||
// 如果需要栅格,需要包裹 ElCol
|
||||
<ElCol {...setGridProp(item.colProps)}>{renderFormItem(item)}</ElCol>
|
||||
) : (
|
||||
renderFormItem(item)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染formItem
|
||||
const renderFormItem = (item: FormSchema) => {
|
||||
// 如果有optionApi,优先使用optionApi, 并且options不存在或者为空数组
|
||||
if (
|
||||
item.optionApi &&
|
||||
(!item.componentProps?.options || !item.componentProps?.options.length)
|
||||
) {
|
||||
// 内部自动调用接口,不影响其它渲染
|
||||
getOptions(item.optionApi, item)
|
||||
}
|
||||
const formItemSlots: Recordable = {
|
||||
default: () => {
|
||||
if (item?.formItemProps?.slots?.default) {
|
||||
return item?.formItemProps?.slots?.default(formModel.value)
|
||||
} else {
|
||||
const Com = componentMap[item.component as string] as ReturnType<typeof defineComponent>
|
||||
|
||||
const { autoSetPlaceholder } = unref(getProps)
|
||||
|
||||
const componentSlots = (item?.componentProps as any)?.slots || {}
|
||||
const slotsMap: Recordable = {
|
||||
...setItemComponentSlots(componentSlots)
|
||||
}
|
||||
// // 如果是select组件,并且没有自定义模板,自动渲染options
|
||||
if (item.component === ComponentNameEnum.SELECT) {
|
||||
slotsMap.default = !componentSlots.default
|
||||
? () => renderSelectOptions(item)
|
||||
: () => {
|
||||
return componentSlots.default(
|
||||
unref((item?.componentProps as SelectComponentProps)?.options)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 虚拟列表
|
||||
if (item.component === ComponentNameEnum.SELECT_V2 && componentSlots.default) {
|
||||
slotsMap.default = ({ item }) => {
|
||||
return componentSlots.default(item)
|
||||
}
|
||||
}
|
||||
|
||||
// 单选框组和按钮样式
|
||||
if (
|
||||
item.component === ComponentNameEnum.RADIO_GROUP ||
|
||||
item.component === ComponentNameEnum.RADIO_BUTTON
|
||||
) {
|
||||
slotsMap.default = !componentSlots.default
|
||||
? () => renderRadioOptions(item)
|
||||
: () => {
|
||||
return componentSlots.default(
|
||||
unref((item?.componentProps as CheckboxGroupComponentProps)?.options)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 多选框组和按钮样式
|
||||
if (
|
||||
item.component === ComponentNameEnum.CHECKBOX_GROUP ||
|
||||
item.component === ComponentNameEnum.CHECKBOX_BUTTON
|
||||
) {
|
||||
slotsMap.default = !componentSlots.default
|
||||
? () => renderCheckboxOptions(item)
|
||||
: () => {
|
||||
return componentSlots.default(
|
||||
unref((item?.componentProps as RadioGroupComponentProps)?.options)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Comp = () => {
|
||||
// 如果field是多层路径,需要转换成对象
|
||||
const itemVal = computed({
|
||||
get: () => {
|
||||
return get(formModel.value, item.field)
|
||||
},
|
||||
set: (val) => {
|
||||
set(formModel.value, item.field, val)
|
||||
}
|
||||
})
|
||||
|
||||
return item.component === ComponentNameEnum.UPLOAD ? (
|
||||
<Com
|
||||
vModel:file-list={itemVal.value}
|
||||
ref={(el: any) => setComponentRefMap(el, item.field)}
|
||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||
{...setComponentProps(item)}
|
||||
style={
|
||||
item.componentProps?.style || {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
>
|
||||
{{ ...slotsMap }}
|
||||
</Com>
|
||||
) : (
|
||||
<Com
|
||||
vModel={itemVal.value}
|
||||
ref={(el: any) => setComponentRefMap(el, item.field)}
|
||||
{...(autoSetPlaceholder && setTextPlaceholder(item))}
|
||||
{...setComponentProps(item)}
|
||||
style={
|
||||
item.componentProps?.style || {
|
||||
width: '100%'
|
||||
}
|
||||
}
|
||||
>
|
||||
{{ ...slotsMap }}
|
||||
</Com>
|
||||
)
|
||||
}
|
||||
|
||||
return <>{Comp()}</>
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item?.formItemProps?.slots?.label) {
|
||||
formItemSlots.label = (...args: any[]) => {
|
||||
return (item?.formItemProps?.slots as any)?.label(...args)
|
||||
}
|
||||
}
|
||||
if (item?.formItemProps?.slots?.error) {
|
||||
formItemSlots.error = (...args: any[]) => {
|
||||
return (item?.formItemProps?.slots as any)?.error(...args)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ElFormItem
|
||||
v-show={!item.hidden}
|
||||
ref={(el: any) => setFormItemRefMap(el, item.field)}
|
||||
{...(item.formItemProps || {})}
|
||||
prop={item.field}
|
||||
label={item.label || ''}
|
||||
>
|
||||
{formItemSlots}
|
||||
</ElFormItem>
|
||||
)
|
||||
}
|
||||
|
||||
// 过滤传入Form组件的属性
|
||||
const getFormBindValue = () => {
|
||||
// 避免在标签上出现多余的属性
|
||||
const delKeys = ['schema', 'isCol', 'autoSetPlaceholder', 'isCustom', 'model']
|
||||
const props = { ...unref(getProps) }
|
||||
for (const key in props) {
|
||||
if (delKeys.indexOf(key) !== -1) {
|
||||
delete props[key]
|
||||
}
|
||||
}
|
||||
return props as FormProps
|
||||
}
|
||||
|
||||
return () => (
|
||||
<ElForm
|
||||
ref={elFormRef}
|
||||
{...getFormBindValue()}
|
||||
model={unref(getProps).isCustom ? unref(getProps).model : formModel}
|
||||
class={prefixCls}
|
||||
// @ts-ignore
|
||||
onSubmit={(e: Event) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
{{
|
||||
// 如果需要自定义,就什么都不渲染,而是提供默认插槽
|
||||
default: () => {
|
||||
const { isCustom } = unref(getProps)
|
||||
return isCustom ? getSlot(slots, 'default') : renderWrap()
|
||||
}
|
||||
}}
|
||||
</ElForm>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.@{elNamespace}-form.@{adminNamespace}-form .@{elNamespace}-row {
|
||||
margin-right: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.@{elNamespace}-form--inline {
|
||||
:deep(.el-form-item__content) {
|
||||
& > :first-child {
|
||||
min-width: 229.5px;
|
||||
}
|
||||
}
|
||||
.@{elNamespace}-input-number {
|
||||
// 229.5px是兼容el-input-number的最小宽度,
|
||||
min-width: 229.5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
import { FormSchema, ComponentNameEnum, CheckboxGroupComponentProps } from '../types'
|
||||
import { ElCheckbox, ElCheckboxButton } from 'element-plus'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export const useRenderCheckbox = () => {
|
||||
const renderCheckboxOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const componentProps = item?.componentProps as CheckboxGroupComponentProps
|
||||
const valueAlias = componentProps?.props?.value || 'value'
|
||||
const labelAlias = componentProps?.props?.label || 'label'
|
||||
const disabledAlias = componentProps?.props?.disabled || 'disabled'
|
||||
const Com = (
|
||||
item.component === ComponentNameEnum.CHECKBOX_GROUP ? ElCheckbox : ElCheckboxButton
|
||||
) as ReturnType<typeof defineComponent>
|
||||
return componentProps?.options?.map((option) => {
|
||||
const { ...other } = option
|
||||
return (
|
||||
<Com
|
||||
{...other}
|
||||
disabled={option[disabledAlias || 'disabled']}
|
||||
label={option[labelAlias || 'label']}
|
||||
value={option[valueAlias || 'value']}
|
||||
></Com>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
renderCheckboxOptions
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { FormSchema, ComponentNameEnum, RadioGroupComponentProps } from '../types'
|
||||
import { ElRadio, ElRadioButton } from 'element-plus'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export const useRenderRadio = () => {
|
||||
const renderRadioOptions = (item: FormSchema) => {
|
||||
// 如果有别名,就取别名
|
||||
const componentProps = item?.componentProps as RadioGroupComponentProps
|
||||
const valueAlias = componentProps?.props?.value || 'value'
|
||||
const labelAlias = componentProps?.props?.label || 'label'
|
||||
const disabledAlias = componentProps?.props?.disabled || 'disabled'
|
||||
const Com = (
|
||||
item.component === ComponentNameEnum.RADIO_GROUP ? ElRadio : ElRadioButton
|
||||
) as ReturnType<typeof defineComponent>
|
||||
return componentProps?.options?.map((option) => {
|
||||
const { ...other } = option
|
||||
return (
|
||||
<Com
|
||||
{...other}
|
||||
disabled={option[disabledAlias || 'disabled']}
|
||||
label={option[labelAlias || 'label']}
|
||||
value={option[valueAlias || 'value']}
|
||||
></Com>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
renderRadioOptions
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ElOption, ElOptionGroup } from 'element-plus'
|
||||
import { FormSchema, SelectComponentProps, SelectOption } from '../types'
|
||||
|
||||
export const useRenderSelect = () => {
|
||||
// 渲染 select options
|
||||
const renderSelectOptions = (item: FormSchema) => {
|
||||
const componentsProps = item?.componentProps as SelectComponentProps
|
||||
const optionGroupDefaultSlot = componentsProps?.slots?.optionGroupDefault
|
||||
// 如果有别名,就取别名
|
||||
const labelAlias = componentsProps?.props?.label
|
||||
const keyAlias = componentsProps?.props?.key
|
||||
return componentsProps?.options?.map((option) => {
|
||||
if (option?.options?.length) {
|
||||
return optionGroupDefaultSlot ? (
|
||||
optionGroupDefaultSlot(option)
|
||||
) : (
|
||||
<ElOptionGroup label={option[labelAlias || 'label']} key={option[keyAlias || 'key']}>
|
||||
{{
|
||||
default: () =>
|
||||
option?.options?.map((v) => {
|
||||
return renderSelectOptionItem(item, v)
|
||||
})
|
||||
}}
|
||||
</ElOptionGroup>
|
||||
)
|
||||
} else {
|
||||
return renderSelectOptionItem(item, option)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染 select option item
|
||||
const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => {
|
||||
// 如果有别名,就取别名
|
||||
const componentsProps = item.componentProps as SelectComponentProps
|
||||
const labelAlias = componentsProps?.props?.label
|
||||
const valueAlias = componentsProps?.props?.value
|
||||
const keyAlias = componentsProps?.props?.key
|
||||
const optionDefaultSlot = componentsProps.slots?.optionDefault
|
||||
|
||||
return (
|
||||
<ElOption
|
||||
{...option}
|
||||
key={option[keyAlias || 'key']}
|
||||
label={option[labelAlias || 'label']}
|
||||
value={option[valueAlias || 'value']}
|
||||
>
|
||||
{{
|
||||
default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined)
|
||||
}}
|
||||
</ElOption>
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
renderSelectOptions
|
||||
}
|
||||
}
|
||||
59
promo-ui2/src/components/Form/src/helper/componentMap.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { Component } from 'vue'
|
||||
import {
|
||||
ElCascader,
|
||||
ElCheckboxGroup,
|
||||
ElColorPicker,
|
||||
ElDatePicker,
|
||||
ElInput,
|
||||
ElInputNumber,
|
||||
ElRadioGroup,
|
||||
ElRate,
|
||||
ElSelect,
|
||||
ElSelectV2,
|
||||
ElSlider,
|
||||
ElSwitch,
|
||||
ElTimePicker,
|
||||
ElTimeSelect,
|
||||
ElTransfer,
|
||||
ElAutocomplete,
|
||||
ElDivider,
|
||||
ElTreeSelect,
|
||||
ElUpload
|
||||
} from 'element-plus'
|
||||
import { InputPassword } from '@/components/InputPassword'
|
||||
import { Editor } from '@/components/Editor'
|
||||
import { JsonEditor } from '@/components/JsonEditor'
|
||||
import { IconPicker } from '@/components/IconPicker'
|
||||
import { IAgree } from '@/components/IAgree'
|
||||
import { ComponentName } from '../types'
|
||||
|
||||
const componentMap: Recordable<Component, ComponentName> = {
|
||||
RadioGroup: ElRadioGroup,
|
||||
RadioButton: ElRadioGroup,
|
||||
CheckboxGroup: ElCheckboxGroup,
|
||||
CheckboxButton: ElCheckboxGroup,
|
||||
Input: ElInput,
|
||||
Autocomplete: ElAutocomplete,
|
||||
InputNumber: ElInputNumber,
|
||||
Select: ElSelect,
|
||||
Cascader: ElCascader,
|
||||
Switch: ElSwitch,
|
||||
Slider: ElSlider,
|
||||
TimePicker: ElTimePicker,
|
||||
DatePicker: ElDatePicker,
|
||||
Rate: ElRate,
|
||||
ColorPicker: ElColorPicker,
|
||||
Transfer: ElTransfer,
|
||||
Divider: ElDivider,
|
||||
TimeSelect: ElTimeSelect,
|
||||
SelectV2: ElSelectV2,
|
||||
InputPassword: InputPassword,
|
||||
Editor: Editor,
|
||||
TreeSelect: ElTreeSelect,
|
||||
Upload: ElUpload,
|
||||
JsonEditor: JsonEditor,
|
||||
IconPicker: IconPicker,
|
||||
IAgree: IAgree
|
||||
}
|
||||
|
||||
export { componentMap }
|
||||
169
promo-ui2/src/components/Form/src/helper/index.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { PlaceholderModel, FormSchema, ComponentNameEnum, ColProps } from '../types'
|
||||
import { isFunction } from '@/utils/is'
|
||||
import { firstUpperCase, humpToDash } from '@/utils'
|
||||
import { set, get } from 'lodash-es'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
/**
|
||||
*
|
||||
* @param schema 对应组件数据
|
||||
* @returns 返回提示信息对象
|
||||
* @description 用于自动设置placeholder
|
||||
*/
|
||||
export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => {
|
||||
const textMap = [
|
||||
ComponentNameEnum.INPUT,
|
||||
ComponentNameEnum.AUTOCOMPLETE,
|
||||
ComponentNameEnum.INPUT_NUMBER,
|
||||
ComponentNameEnum.INPUT_PASSWORD
|
||||
]
|
||||
const selectMap = [
|
||||
ComponentNameEnum.SELECT,
|
||||
ComponentNameEnum.TIME_PICKER,
|
||||
ComponentNameEnum.DATE_PICKER,
|
||||
ComponentNameEnum.TIME_SELECT,
|
||||
ComponentNameEnum.SELECT_V2
|
||||
]
|
||||
if (textMap.includes(schema?.component as ComponentNameEnum)) {
|
||||
return {
|
||||
placeholder: t('common.inputText')
|
||||
}
|
||||
}
|
||||
if (selectMap.includes(schema?.component as ComponentNameEnum)) {
|
||||
// 一些范围选择器
|
||||
const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange']
|
||||
if (
|
||||
twoTextMap.includes(
|
||||
((schema?.componentProps as any)?.type ||
|
||||
(schema?.componentProps as any)?.isRange) as string
|
||||
)
|
||||
) {
|
||||
return {
|
||||
startPlaceholder: t('common.startTimeText'),
|
||||
endPlaceholder: t('common.endTimeText'),
|
||||
rangeSeparator: '-'
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
placeholder: t('common.selectText')
|
||||
}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param col 内置栅格
|
||||
* @returns 返回栅格属性
|
||||
* @description 合并传入进来的栅格属性
|
||||
*/
|
||||
export const setGridProp = (col: ColProps = {}): ColProps => {
|
||||
const colProps: ColProps = {
|
||||
// 如果有span,代表用户优先级更高,所以不需要默认栅格
|
||||
...(col.span
|
||||
? {}
|
||||
: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 12,
|
||||
lg: 12,
|
||||
xl: 12
|
||||
}),
|
||||
...col
|
||||
}
|
||||
return colProps
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param item 传入的组件属性
|
||||
* @returns 默认添加 clearable 属性
|
||||
*/
|
||||
export const setComponentProps = (item: FormSchema): Recordable => {
|
||||
// const notNeedClearable = ['ColorPicker']
|
||||
// 拆分事件并组合
|
||||
const onEvents = (item?.componentProps as any)?.on || {}
|
||||
const newOnEvents: Recordable = {}
|
||||
|
||||
for (const key in onEvents) {
|
||||
if (onEvents[key]) {
|
||||
newOnEvents[`on${firstUpperCase(key)}`] = (...args: any[]) => {
|
||||
onEvents[key](...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const componentProps: Recordable = {
|
||||
clearable: true,
|
||||
...item.componentProps,
|
||||
...newOnEvents
|
||||
}
|
||||
// 需要删除额外的属性
|
||||
if (componentProps.slots) {
|
||||
delete componentProps.slots
|
||||
}
|
||||
if (componentProps.on) {
|
||||
delete componentProps.on
|
||||
}
|
||||
return componentProps
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param formModel 表单数据
|
||||
* @param slotsProps 插槽属性
|
||||
*/
|
||||
export const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable => {
|
||||
const slotObj: Recordable = {}
|
||||
for (const key in slotsProps) {
|
||||
if (slotsProps[key]) {
|
||||
if (isFunction(slotsProps[key])) {
|
||||
slotObj[humpToDash(key)] = (...args: any[]) => {
|
||||
return slotsProps[key]?.(...args)
|
||||
}
|
||||
} else {
|
||||
slotObj[humpToDash(key)] = () => {
|
||||
return slotsProps[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return slotObj
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param schema Form表单结构化数组
|
||||
* @param formModel FormMoel
|
||||
* @returns FormMoel
|
||||
* @description 生成对应的formModel
|
||||
*/
|
||||
export const initModel = (schema: FormSchema[], formModel: Recordable) => {
|
||||
const model: Recordable = { ...formModel }
|
||||
schema.map((v) => {
|
||||
if (v.remove) {
|
||||
delete model[v.field]
|
||||
} else if (v.component !== 'Divider') {
|
||||
// const hasField = Reflect.has(model, v.field)
|
||||
const hasField = get(model, v.field)
|
||||
// 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值
|
||||
set(
|
||||
model,
|
||||
v.field,
|
||||
hasField !== void 0 ? get(model, v.field) : v.value !== void 0 ? v.value : undefined
|
||||
)
|
||||
// model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : undefined
|
||||
}
|
||||
})
|
||||
// 如果 schema 对应的 field 不存在,则删除 model 中的对应的 field
|
||||
for (let i = 0; i < schema.length; i++) {
|
||||
const key = schema[i].field
|
||||
if (!Object.prototype.hasOwnProperty.call(model, key)) {
|
||||
delete model[key]
|
||||
}
|
||||
}
|
||||
return model
|
||||
}
|
||||
670
promo-ui2/src/components/Form/src/types/index.ts
Normal file
@@ -0,0 +1,670 @@
|
||||
import {
|
||||
AutocompleteProps,
|
||||
InputNumberProps,
|
||||
CascaderProps,
|
||||
CascaderNode,
|
||||
CascaderValue,
|
||||
SwitchProps,
|
||||
ComponentSize,
|
||||
InputProps,
|
||||
RateProps,
|
||||
ColorPickerProps,
|
||||
TransferProps,
|
||||
RadioGroupProps,
|
||||
RadioButtonProps,
|
||||
CheckboxGroupProps,
|
||||
DividerProps,
|
||||
DatePickerProps,
|
||||
FormItemProps as ElFormItemProps,
|
||||
FormProps as ElFormProps,
|
||||
ISelectProps,
|
||||
UploadProps
|
||||
} from 'element-plus'
|
||||
import { IEditorConfig } from '@wangeditor/editor'
|
||||
import { JsonEditorProps } from '@/components/JsonEditor'
|
||||
import { IAgreeProps } from '@/components/IAgree'
|
||||
import { CSSProperties } from 'vue'
|
||||
|
||||
export interface PlaceholderModel {
|
||||
placeholder?: string
|
||||
startPlaceholder?: string
|
||||
endPlaceholder?: string
|
||||
rangeSeparator?: string
|
||||
}
|
||||
|
||||
export enum ComponentNameEnum {
|
||||
RADIO_GROUP = 'RadioGroup',
|
||||
RADIO_BUTTON = 'RadioButton',
|
||||
CHECKBOX_GROUP = 'CheckboxGroup',
|
||||
CHECKBOX_BUTTON = 'CheckboxButton',
|
||||
INPUT = 'Input',
|
||||
AUTOCOMPLETE = 'Autocomplete',
|
||||
INPUT_NUMBER = 'InputNumber',
|
||||
SELECT = 'Select',
|
||||
CASCADER = 'Cascader',
|
||||
SWITCH = 'Switch',
|
||||
SLIDER = 'Slider',
|
||||
TIME_PICKER = 'TimePicker',
|
||||
DATE_PICKER = 'DatePicker',
|
||||
RATE = 'Rate',
|
||||
COLOR_PICKER = 'ColorPicker',
|
||||
TRANSFER = 'Transfer',
|
||||
DIVIDER = 'Divider',
|
||||
TIME_SELECT = 'TimeSelect',
|
||||
SELECT_V2 = 'SelectV2',
|
||||
INPUT_PASSWORD = 'InputPassword',
|
||||
EDITOR = 'Editor',
|
||||
TREE_SELECT = 'TreeSelect',
|
||||
UPLOAD = 'Upload',
|
||||
JSON_EDITOR = 'JsonEditor',
|
||||
ICON_PICKER = 'IconPicker',
|
||||
I_AGREE = 'IAgree'
|
||||
}
|
||||
|
||||
type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K
|
||||
? K extends string
|
||||
? K extends `${infer A}_${infer B}`
|
||||
? `${Capitalize<Lowercase<A>>}${Capitalize<Lowercase<B>>}`
|
||||
: Capitalize<Lowercase<K>>
|
||||
: never
|
||||
: never
|
||||
|
||||
export type ComponentName = CamelCaseComponentName
|
||||
|
||||
export interface InputPasswordComponentProps {
|
||||
strength?: boolean
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface InputComponentProps extends Partial<InputProps> {
|
||||
rows?: number
|
||||
on?: {
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
change?: (value: string | number) => void
|
||||
clear?: () => void
|
||||
input?: (value: string | number) => void
|
||||
}
|
||||
slots?: {
|
||||
prefix?: (...args: any[]) => JSX.Element | null
|
||||
suffix?: (...args: any[]) => JSX.Element | null
|
||||
prepend?: (...args: any[]) => JSX.Element | null
|
||||
append?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface AutocompleteComponentProps extends Partial<AutocompleteProps> {
|
||||
on?: {
|
||||
select?: (item: any) => void
|
||||
change?: (value: string | number) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
prefix?: (...args: any[]) => JSX.Element | null
|
||||
suffix?: (...args: any[]) => JSX.Element | null
|
||||
prepend?: (...args: any[]) => JSX.Element | null
|
||||
append?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface InputNumberComponentProps extends Partial<InputNumberProps> {
|
||||
on?: {
|
||||
change?: (currentValue: number | undefined, oldValue: number | undefined) => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface SelectOption {
|
||||
label?: string
|
||||
disabled?: boolean
|
||||
value?: any
|
||||
key?: string | number
|
||||
options?: SelectOption[]
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface SelectComponentProps extends Omit<Partial<ISelectProps>, 'options'> {
|
||||
/**
|
||||
* 数据源的字段别名
|
||||
*/
|
||||
props?: {
|
||||
key?: string
|
||||
value?: string
|
||||
label?: string
|
||||
children?: string
|
||||
}
|
||||
on?: {
|
||||
change?: (value: string | number | boolean | object) => void
|
||||
visibleChange?: (visible: boolean) => void
|
||||
removeTag?: (tag: any) => void
|
||||
clear?: () => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (options: SelectOption[]) => JSX.Element[] | null
|
||||
optionGroupDefault?: (item: SelectOption) => JSX.Element
|
||||
optionDefault?: (option: SelectOption) => JSX.Element | null
|
||||
prefix?: (...args: any[]) => JSX.Element | null
|
||||
empty?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
options?: SelectOption[]
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface SelectV2ComponentProps {
|
||||
multiple?: boolean
|
||||
disabled?: boolean
|
||||
valueKey?: string
|
||||
size?: ComponentSize
|
||||
clearable?: boolean
|
||||
clearIcon?: string | JSX.Element | null
|
||||
collapseTags?: boolean
|
||||
multipleLimit?: number
|
||||
name?: string
|
||||
effect?: string
|
||||
autocomplete?: string
|
||||
placeholder?: string
|
||||
filterable?: boolean
|
||||
allowCreate?: boolean
|
||||
reserveKeyword?: boolean
|
||||
noDataText?: string
|
||||
popperClass?: string
|
||||
teleported?: boolean
|
||||
persistent?: boolean
|
||||
popperOptions?: any
|
||||
automaticDropdown?: boolean
|
||||
height?: number
|
||||
scrollbarAlwaysOn?: boolean
|
||||
remote?: boolean
|
||||
remoteMethod?: (query: string) => void
|
||||
validateEvent?: boolean
|
||||
placement?: AutocompleteProps['placement']
|
||||
collapseTagsTooltip?: boolean
|
||||
on?: {
|
||||
change?: (value: string | number | boolean | object) => void
|
||||
visibleChange?: (visible: boolean) => void
|
||||
removeTag?: (tag: any) => void
|
||||
clear?: () => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
}
|
||||
options?: SelectOption[]
|
||||
slots?: {
|
||||
default?: (option: SelectOption) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface CascaderComponentProps {
|
||||
options?: Record<string, unknown>[]
|
||||
props?: CascaderProps
|
||||
size?: ComponentSize
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
clearable?: boolean
|
||||
showAllLevels?: boolean
|
||||
collapseTags?: boolean
|
||||
collapseTagsTooltip?: boolean
|
||||
separator?: string
|
||||
filterable?: boolean
|
||||
filterMethod?: (node: CascaderNode, keyword: string) => boolean
|
||||
debounce?: number
|
||||
beforeFilter?: (value: string) => boolean
|
||||
popperClass?: string
|
||||
teleported?: boolean
|
||||
tagType?: ElementPlusInfoType
|
||||
validateEvent?: boolean
|
||||
on?: {
|
||||
change?: (value: CascaderValue) => void
|
||||
expandChange?: (value: CascaderValue) => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
visibleChange?: (value: boolean) => void
|
||||
removeTag?: (value: CascaderNode['valueByOption']) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
empty?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface SwitchComponentProps extends Partial<SwitchProps> {
|
||||
on?: {
|
||||
change?: (value: boolean | string | number) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface RateComponentProps extends Partial<RateProps> {
|
||||
on?: {
|
||||
change?: (value: number) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface ColorPickerComponentProps extends Partial<ColorPickerProps> {
|
||||
on?: {
|
||||
change?: (value: string) => void
|
||||
activeChange?: (value: string) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface TransferComponentProps extends Partial<TransferProps> {
|
||||
on?: {
|
||||
change?: (
|
||||
value: number | string,
|
||||
direction: 'left' | 'right',
|
||||
movedKeys: string[] | number[]
|
||||
) => void
|
||||
leftCheckChange?: (value: any[]) => void
|
||||
rightCheckChange?: (value: any[]) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
leftFooter?: (...args: any[]) => JSX.Element | null
|
||||
rightFooter?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface RadioOption {
|
||||
label?: string
|
||||
value?: string | number | boolean
|
||||
disabled?: boolean
|
||||
border?: boolean
|
||||
size?: ComponentSize
|
||||
name?: string
|
||||
[key: string]: any
|
||||
}
|
||||
export interface RadioGroupComponentProps extends Partial<RadioGroupProps> {
|
||||
options?: RadioOption[]
|
||||
/**
|
||||
* 数据源的字段别名
|
||||
*/
|
||||
props?: {
|
||||
label?: string
|
||||
value?: string
|
||||
disabled?: string
|
||||
}
|
||||
on?: {
|
||||
change?: (value: string | number | boolean) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element[] | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface RadioButtonComponentProps extends Partial<RadioButtonProps> {
|
||||
options?: RadioOption[]
|
||||
/**
|
||||
* 数据源的字段别名
|
||||
*/
|
||||
props?: {
|
||||
label?: string
|
||||
value?: string
|
||||
disabled?: string
|
||||
}
|
||||
on?: {
|
||||
change?: (value: string | number | boolean) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element[] | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface CheckboxOption {
|
||||
label?: string
|
||||
value?: string | number | boolean
|
||||
disabled?: boolean
|
||||
trueLabel?: string | number
|
||||
falseLabel?: string | number
|
||||
border?: boolean
|
||||
size?: ComponentSize
|
||||
name?: string
|
||||
checked?: boolean
|
||||
indeterminate?: boolean
|
||||
validateEvent?: boolean
|
||||
tabindex?: number | string
|
||||
id?: string
|
||||
controls?: boolean
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface CheckboxGroupComponentProps extends Partial<CheckboxGroupProps> {
|
||||
options?: CheckboxOption[]
|
||||
/**
|
||||
* 数据源的字段别名
|
||||
*/
|
||||
props?: {
|
||||
label?: string
|
||||
value?: string
|
||||
disabled?: string
|
||||
}
|
||||
on?: {
|
||||
change?: (value: string | number | boolean) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element[] | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface DividerComponentProps extends Partial<DividerProps> {
|
||||
on?: {
|
||||
change?: (value: number) => void
|
||||
input?: (value: number) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface DatePickerComponentProps extends Partial<DatePickerProps> {
|
||||
on?: {
|
||||
change?: (value: string | Date | number | string[]) => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
calendarChange?: (val: [Date, Date]) => void
|
||||
panelChange?: (date, mode, view) => void
|
||||
visibleChange?: (visibility: boolean) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
rangeSeparator?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface DateTimePickerComponentProps {
|
||||
readonly?: boolean
|
||||
disabled?: boolean
|
||||
editable?: boolean
|
||||
clearable?: boolean
|
||||
size?: ComponentSize
|
||||
placeholder?: string
|
||||
startPlaceholder?: string
|
||||
endPlaceholder?: string
|
||||
timeArrowControl?: boolean
|
||||
type?: 'year' | 'month' | 'date' | 'datetime' | 'datetimerange' | 'daterange' | 'week'
|
||||
format?: string
|
||||
popperClass?: string
|
||||
rangeSeparator?: string
|
||||
defaultValue?: Date | [Date, Date]
|
||||
defaultTime?: Date | [Date, Date]
|
||||
valueFormat?: string
|
||||
id?: string
|
||||
name?: string
|
||||
unlinkPanels?: boolean
|
||||
prefixIcon?: string | JSX.Element
|
||||
clearIcon?: string | JSX.Element
|
||||
shortcuts?: Array<{ text: string; value: Date | Function }>
|
||||
disabledDate?: (date: Date) => boolean
|
||||
cellClassName?: string | ((date: Date) => string | undefined)
|
||||
teleported?: boolean
|
||||
on?: {
|
||||
change?: (value: string | Date | number | string[]) => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
calendarChange?: (val: [Date, Date]) => void
|
||||
visibleChange?: (visibility: boolean) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
rangeSeparator?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface TimePickerComponentProps {
|
||||
readonly?: boolean
|
||||
disabled?: boolean
|
||||
editable?: boolean
|
||||
clearable?: boolean
|
||||
size?: ComponentSize
|
||||
placeholder?: string
|
||||
startPlaceholder?: string
|
||||
endPlaceholder?: string
|
||||
isRange?: boolean
|
||||
arrowControl?: boolean
|
||||
popperClass?: string
|
||||
rangeSeparator?: string
|
||||
format?: string
|
||||
defaultValue?: Date | [Date, Date]
|
||||
id?: string
|
||||
name?: string
|
||||
label?: string
|
||||
prefixIcon?: string | JSX.Element
|
||||
clearIcon?: string | JSX.Element
|
||||
disabledHours?: (role: string, comparingDate?: any) => number[]
|
||||
disabledMinutes?: (hour: number, role: string, comparingDate?: any) => number[]
|
||||
disabledSeconds?: (hour: number, minute: number, role: string, comparingDate?: any) => number[]
|
||||
teleported?: boolean
|
||||
tabindex?: number | string
|
||||
on?: {
|
||||
change: (
|
||||
val: number | string | Date | [number, number] | [string, string] | [Date, Date]
|
||||
) => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
visibleChange?: (visibility: boolean) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface TimeSelectComponentProps {
|
||||
disabled?: boolean
|
||||
editable?: boolean
|
||||
clearable?: boolean
|
||||
size?: ComponentSize
|
||||
placeholder?: string
|
||||
name?: string
|
||||
effect?: string
|
||||
prefixIcon?: string | JSX.Element
|
||||
clearIcon?: string | JSX.Element
|
||||
start?: string
|
||||
end?: string
|
||||
step?: string
|
||||
minTime?: string
|
||||
maxTime?: string
|
||||
format?: string
|
||||
on?: {
|
||||
change?: (val: string) => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface EditorComponentProps {
|
||||
editorConfig?: IEditorConfig
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface ColProps {
|
||||
span?: number
|
||||
xs?: number
|
||||
sm?: number
|
||||
md?: number
|
||||
lg?: number
|
||||
xl?: number
|
||||
tag?: string
|
||||
}
|
||||
|
||||
export interface FormSetProps {
|
||||
field: string
|
||||
path: string
|
||||
value: any
|
||||
}
|
||||
|
||||
export interface FormItemProps extends Partial<ElFormItemProps> {
|
||||
style?: CSSProperties
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
label?: (...args: any[]) => JSX.Element | null
|
||||
error?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface UploadComponentProps extends Partial<UploadProps> {
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
trigger?: (...args: any[]) => JSX.Element | null
|
||||
tip?: (...args: any[]) => JSX.Element | null
|
||||
file?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface TreeSelectComponentProps
|
||||
extends Omit<Partial<SelectComponentProps>, 'props' | 'on' | 'slots'> {
|
||||
data?: any[]
|
||||
emptyText?: string
|
||||
nodeKey?: string
|
||||
props?: {
|
||||
children?: string
|
||||
label?: string | ((...args: any[]) => string)
|
||||
disabled?: string | ((...args: any[]) => string)
|
||||
isLeaf?: string | ((...args: any[]) => string)
|
||||
class?: string | ((...args: any[]) => string)
|
||||
}
|
||||
renderAfterExpand?: boolean
|
||||
load?: (...args: any[]) => Promise<any>
|
||||
renderContent?: (...args: any[]) => JSX.Element | null
|
||||
highlightCurrent?: boolean
|
||||
defaultExpandAll?: boolean
|
||||
expandOnClickNode?: boolean
|
||||
checkOnClickNode?: boolean
|
||||
autoExpandParent?: boolean
|
||||
defaultExpandedKeys?: any[]
|
||||
showCheckbox?: boolean
|
||||
checkStrictly?: boolean
|
||||
defaultCheckedKeys?: any[]
|
||||
currentNodeKey?: string | number
|
||||
filterNodeMethod?: (...args: any[]) => boolean
|
||||
accordion?: boolean
|
||||
indent?: number
|
||||
icon?: string | ((...args: any[]) => JSX.Element | null)
|
||||
lazy?: boolean
|
||||
draggable?: boolean
|
||||
allowDrag?: (...args: any[]) => boolean
|
||||
allowDrop?: (...args: any[]) => boolean
|
||||
on?: {
|
||||
change?: (value: string | number | boolean | object) => void
|
||||
visibleChange?: (visible: boolean) => void
|
||||
removeTag?: (tag: any) => void
|
||||
clear?: () => void
|
||||
blur?: (event: FocusEvent) => void
|
||||
focus?: (event: FocusEvent) => void
|
||||
nodeClick?: (...args: any[]) => void
|
||||
nodeContextMenu?: (...args: any[]) => void
|
||||
checkChange?: (...args: any[]) => void
|
||||
check?: (...args: any[]) => void
|
||||
currentChange?: (...args: any[]) => void
|
||||
nodeExpand?: (...args: any[]) => void
|
||||
nodeCollapse?: (...args: any[]) => void
|
||||
nodeDragStart?: (...args: any[]) => void
|
||||
nodeDragEnter?: (...args: any[]) => void
|
||||
nodeDragLeave?: (...args: any[]) => void
|
||||
nodeDragOver?: (...args: any[]) => void
|
||||
nodeDragEnd?: (...args: any[]) => void
|
||||
nodeDrop?: (...args: any[]) => void
|
||||
}
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
optionGroupDefault?: (item: SelectOption) => JSX.Element
|
||||
optionDefault?: (option: SelectOption) => JSX.Element | null
|
||||
prefix?: (...args: any[]) => JSX.Element | null
|
||||
empty?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface FormSchema {
|
||||
/**
|
||||
* 唯一标识
|
||||
*/
|
||||
field: string
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
label?: string
|
||||
|
||||
/**
|
||||
* col组件属性
|
||||
*/
|
||||
colProps?: ColProps
|
||||
|
||||
/**
|
||||
* 表单组件属性,具体可以查看element-plus文档
|
||||
*/
|
||||
componentProps?:
|
||||
| InputComponentProps
|
||||
| AutocompleteComponentProps
|
||||
| InputNumberComponentProps
|
||||
| SelectComponentProps
|
||||
| SelectV2ComponentProps
|
||||
| CascaderComponentProps
|
||||
| SwitchComponentProps
|
||||
| RateComponentProps
|
||||
| ColorPickerComponentProps
|
||||
| TransferComponentProps
|
||||
| RadioGroupComponentProps
|
||||
| RadioButtonComponentProps
|
||||
| DividerComponentProps
|
||||
| DatePickerComponentProps
|
||||
| DateTimePickerComponentProps
|
||||
| TimePickerComponentProps
|
||||
| InputPasswordComponentProps
|
||||
| TreeSelectComponentProps
|
||||
| UploadComponentProps
|
||||
| JsonEditorProps
|
||||
| IAgreeProps
|
||||
| any
|
||||
|
||||
/**
|
||||
* formItem组件属性,具体可以查看element-plus文档
|
||||
*/
|
||||
formItemProps?: FormItemProps
|
||||
|
||||
/**
|
||||
* 渲染的组件名称
|
||||
*/
|
||||
component?: ComponentName
|
||||
|
||||
/**
|
||||
* 初始值
|
||||
*/
|
||||
value?: any
|
||||
|
||||
/**
|
||||
* 是否隐藏,如果为true,会连同值一同删除,类似v-if
|
||||
*/
|
||||
remove?: boolean
|
||||
|
||||
/**
|
||||
* 样式隐藏,不会把值一同删掉,类似v-show
|
||||
*/
|
||||
hidden?: boolean
|
||||
|
||||
/**
|
||||
* @returns 远程加载下拉项
|
||||
*/
|
||||
optionApi?: any
|
||||
}
|
||||
|
||||
export interface FormProps extends Partial<ElFormProps> {
|
||||
schema?: FormSchema[]
|
||||
isCol?: boolean
|
||||
model?: Recordable
|
||||
autoSetPlaceholder?: boolean
|
||||
isCustom?: boolean
|
||||
[key: string]: any
|
||||
}
|
||||
3
promo-ui2/src/components/Highlight/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Highlight from './src/Highlight.vue'
|
||||
|
||||
export { Highlight }
|
||||
65
promo-ui2/src/components/Highlight/src/Highlight.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, PropType, computed, h, unref } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Highlight',
|
||||
props: {
|
||||
tag: propTypes.string.def('span'),
|
||||
keys: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
color: propTypes.string.def('var(--el-color-primary)')
|
||||
},
|
||||
emits: ['click'],
|
||||
setup(props, { emit, slots }) {
|
||||
const keyNodes = computed(() => {
|
||||
return props.keys.map((key) => {
|
||||
return h(
|
||||
'span',
|
||||
{
|
||||
onClick: () => {
|
||||
emit('click', key)
|
||||
},
|
||||
style: {
|
||||
color: props.color,
|
||||
cursor: 'pointer'
|
||||
}
|
||||
},
|
||||
key
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const parseText = (text: string) => {
|
||||
props.keys.forEach((key, index) => {
|
||||
const regexp = new RegExp(key, 'g')
|
||||
text = text.replace(regexp, `{{${index}}}`)
|
||||
})
|
||||
return text.split(/{{|}}/)
|
||||
}
|
||||
|
||||
const renderText = () => {
|
||||
if (!slots?.default) return null
|
||||
const node = slots?.default()[0].children
|
||||
|
||||
if (!node) {
|
||||
return slots?.default()[0]
|
||||
}
|
||||
|
||||
const textArray = parseText(node as string)
|
||||
const regexp = /^[0-9]*$/
|
||||
const nodes = textArray.map((t) => {
|
||||
if (regexp.test(t)) {
|
||||
return unref(keyNodes)[t] || t
|
||||
}
|
||||
return t
|
||||
})
|
||||
return h(props.tag, nodes)
|
||||
}
|
||||
|
||||
return () => renderText()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
4
promo-ui2/src/components/IAgree/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import IAgree from './src/IAgree.vue'
|
||||
|
||||
export type { LinkItem, IAgreeProps } from './src/types'
|
||||
export { IAgree }
|
||||
41
promo-ui2/src/components/IAgree/src/IAgree.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang="ts" setup>
|
||||
import { ElCheckbox } from 'element-plus'
|
||||
import { Highlight } from '@/components/Highlight'
|
||||
import { PropType, computed } from 'vue'
|
||||
import { LinkItem } from './types'
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
link: {
|
||||
type: Array as PropType<LinkItem[]>,
|
||||
default: undefined
|
||||
}
|
||||
})
|
||||
|
||||
const modelValue = defineModel<boolean>()
|
||||
|
||||
const highlightKeys = computed(() => {
|
||||
return props.link?.map((item) => item.text) || []
|
||||
})
|
||||
|
||||
const keyClick = (key: string) => {
|
||||
const linkItem = props.link?.find((item) => item.text === key)
|
||||
if (linkItem?.url) {
|
||||
window.open(linkItem.url)
|
||||
return
|
||||
}
|
||||
if (linkItem?.onClick) {
|
||||
linkItem.onClick()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<ElCheckbox v-model="modelValue" class="mr-0px!" />
|
||||
<Highlight class="ml-10px" :keys="highlightKeys" @click="keyClick">{{ text }}</Highlight>
|
||||
</div>
|
||||
</template>
|
||||
10
promo-ui2/src/components/IAgree/src/types/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export interface LinkItem {
|
||||
text: string
|
||||
url?: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export interface IAgreeProps {
|
||||
text: string
|
||||
link: LinkItem[]
|
||||
}
|
||||
5
promo-ui2/src/components/Icon/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import Icon from './src/Icon.vue'
|
||||
|
||||
export type { IconTypes } from './src/types'
|
||||
|
||||
export { Icon }
|
||||
79
promo-ui2/src/components/Icon/src/Icon.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, unref } from 'vue'
|
||||
import { ElIcon } from 'element-plus'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { ICON_PREFIX } from '@/constants'
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('icon')
|
||||
|
||||
const props = defineProps({
|
||||
// icon name
|
||||
icon: propTypes.string,
|
||||
// icon color
|
||||
color: propTypes.string,
|
||||
// icon size
|
||||
size: propTypes.number.def(16),
|
||||
hoverColor: propTypes.string
|
||||
})
|
||||
|
||||
const isLocal = computed(() => props.icon.startsWith('svg-icon:'))
|
||||
|
||||
const symbolId = computed(() => {
|
||||
return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon
|
||||
})
|
||||
|
||||
// 是否使用在线图标
|
||||
const isUseOnline = computed(() => {
|
||||
return import.meta.env.VITE_USE_ONLINE_ICON === 'true'
|
||||
})
|
||||
|
||||
const getIconifyStyle = computed(() => {
|
||||
const { color, size } = props
|
||||
return {
|
||||
fontSize: `${size}px`,
|
||||
color
|
||||
}
|
||||
})
|
||||
|
||||
const getIconName = computed(() => {
|
||||
return props.icon.startsWith(ICON_PREFIX) ? props.icon.replace(ICON_PREFIX, '') : props.icon
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElIcon :class="prefixCls" :size="size" :color="color">
|
||||
<svg v-if="isLocal" aria-hidden="true">
|
||||
<use :xlink:href="symbolId" />
|
||||
</svg>
|
||||
|
||||
<template v-else>
|
||||
<Icon v-if="isUseOnline" :icon="getIconName" :style="getIconifyStyle" />
|
||||
<div v-else :class="`${icon} iconify`" :style="getIconifyStyle"></div>
|
||||
</template>
|
||||
</ElIcon>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{adminNamespace}-icon';
|
||||
|
||||
.@{prefix-cls},
|
||||
.iconify {
|
||||
:deep(svg) {
|
||||
&:hover {
|
||||
// stylelint-disable-next-line
|
||||
color: v-bind(hoverColor) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.iconify {
|
||||
&:hover {
|
||||
// stylelint-disable-next-line
|
||||
color: v-bind(hoverColor) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
6
promo-ui2/src/components/Icon/src/types/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface IconTypes {
|
||||
size?: number
|
||||
color?: string
|
||||
icon: string
|
||||
hoverColor?: string
|
||||
}
|
||||
3
promo-ui2/src/components/IconPicker/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import IconPicker from './src/IconPicker.vue'
|
||||
|
||||
export { IconPicker }
|
||||
194
promo-ui2/src/components/IconPicker/src/IconPicker.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<script setup lang="ts">
|
||||
import epIcons from './data/icons.ep'
|
||||
import antIcons from './data/icons.ant-design'
|
||||
import tIcons from './data/icons.tdesign'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { ElInput, ElPopover, ElScrollbar, ElTabs, ElTabPane, ElPagination } from 'element-plus'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { computed, CSSProperties, ref, unref, watch } from 'vue'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
const init = async (icon?: string) => {
|
||||
if (!icon) return
|
||||
const iconInfo = icon.split(':')
|
||||
iconName.value = iconInfo[0]
|
||||
const wrapIndex = icons.findIndex((item) => item.prefix === iconInfo[0])
|
||||
// 查询当前icon的索引
|
||||
const index = filterItemIcons(icons[wrapIndex].icons).findIndex((item) => item === icon)
|
||||
// 计算当前icon的页码
|
||||
await nextTick()
|
||||
currentPage.value = Math.ceil((index + 1) / unref(pageSize))
|
||||
}
|
||||
|
||||
const modelValue = defineModel<string>()
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const size = computed(() => appStore.getCurrentSize)
|
||||
|
||||
const iconSize = computed(() => {
|
||||
return unref(size) === 'small'
|
||||
? 'var(--el-component-size-small)'
|
||||
: unref(size) === 'large'
|
||||
? 'var(--el-component-size-large)'
|
||||
: 'var(--el-component-size)'
|
||||
})
|
||||
|
||||
const iconWrapStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
width: unref(iconSize),
|
||||
height: unref(iconSize),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 0 0 1px var(--el-input-border-color,var(--el-border-color)) inset',
|
||||
position: 'relative',
|
||||
left: '-1px',
|
||||
cursor: 'pointer'
|
||||
}
|
||||
})
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const prefixCls = getPrefixCls('icon-picker')
|
||||
|
||||
const icons = [epIcons, antIcons, tIcons]
|
||||
|
||||
const iconName = ref(icons[0].prefix)
|
||||
|
||||
const currentIconNameIndex = computed(() => {
|
||||
const index = icons.findIndex((item) => item.prefix === unref(iconName))
|
||||
return index < 0 ? 0 : index
|
||||
})
|
||||
|
||||
const tabChange = () => {
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
const pageSize = ref(49)
|
||||
|
||||
const currentPage = ref(1)
|
||||
|
||||
const filterIcons = (icons: string[]) => {
|
||||
const start = (unref(currentPage) - 1) * unref(pageSize)
|
||||
const end = unref(currentPage) * unref(pageSize)
|
||||
return icons.slice(start, end)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => modelValue.value,
|
||||
async (val) => {
|
||||
await nextTick()
|
||||
val && init(val)
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
const popoverShow = () => {
|
||||
init(unref(modelValue))
|
||||
}
|
||||
|
||||
const iconSelect = (icon: string) => {
|
||||
// 如果是同一个icon则不做处理,则相当于点击了清空按钮
|
||||
if (icon === unref(modelValue)) {
|
||||
modelValue.value = ''
|
||||
return
|
||||
}
|
||||
modelValue.value = icon
|
||||
}
|
||||
|
||||
const search = ref('')
|
||||
|
||||
const filterItemIcons = (icons: string[]) => {
|
||||
return icons.filter((item) => item.includes(unref(search)))
|
||||
}
|
||||
|
||||
const inputClear = () => {
|
||||
init(unref(modelValue))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="prefixCls" class="flex justify-center items-center box">
|
||||
<ElInput disabled v-model="modelValue" clearable />
|
||||
<ElPopover
|
||||
placement="bottom"
|
||||
trigger="click"
|
||||
:width="450"
|
||||
popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; height: 380px;"
|
||||
@show="popoverShow"
|
||||
>
|
||||
<template #reference>
|
||||
<div :style="iconWrapStyle">
|
||||
<Icon v-if="modelValue" :icon="modelValue" />
|
||||
</div>
|
||||
</template>
|
||||
<ElScrollbar class="h-[calc(100%-50px)]!">
|
||||
<ElInput
|
||||
v-model="search"
|
||||
class="mb-20px"
|
||||
clearable
|
||||
placeholder="搜索图标"
|
||||
@clear="inputClear"
|
||||
/>
|
||||
<ElTabs tab-position="left" v-model="iconName" @tab-change="tabChange">
|
||||
<ElTabPane v-for="item in icons" :key="item.name" :label="item.name" :name="item.prefix">
|
||||
<div class="flex flex-wrap box-border">
|
||||
<div
|
||||
v-for="icon in filterIcons(filterItemIcons(item.icons))"
|
||||
:key="icon"
|
||||
:style="{
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
border: `1px solid ${
|
||||
icon === modelValue ? 'var(--el-color-primary)' : 'var(--el-border-color)'
|
||||
}`,
|
||||
boxSizing: 'border-box',
|
||||
margin: '2px',
|
||||
transition: 'all 0.3s'
|
||||
}"
|
||||
class="hover:border-color-[var(--el-color-primary)]!"
|
||||
@click="iconSelect(icon)"
|
||||
>
|
||||
<Icon
|
||||
:icon="icon"
|
||||
:color="icon === modelValue ? 'var(--el-color-primary)' : 'inherit'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</ElScrollbar>
|
||||
<div
|
||||
class="h-50px absolute bottom-0 left-0 flex items-center pl-[var(--el-popover-padding)] pr-[var(--el-popover-padding)]"
|
||||
>
|
||||
<ElPagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:pager-count="5"
|
||||
size="small"
|
||||
:page-sizes="[100, 200, 300, 400]"
|
||||
layout="total, prev, pager, next, jumper"
|
||||
:total="filterItemIcons(icons[currentIconNameIndex].icons).length"
|
||||
/>
|
||||
</div>
|
||||
</ElPopover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{adminNamespace}-icon-picker';
|
||||
|
||||
.@{prefix-cls} {
|
||||
:deep(.@{elNamespace}-input__wrapper) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
836
promo-ui2/src/components/IconPicker/src/data/icons.ant-design.ts
Normal file
@@ -0,0 +1,836 @@
|
||||
export default {
|
||||
name: 'Ant Design Icons',
|
||||
prefix: 'vi-ant-design',
|
||||
icons: [
|
||||
'vi-ant-design:account-book-filled',
|
||||
'vi-ant-design:account-book-outlined',
|
||||
'vi-ant-design:account-book-twotone',
|
||||
'vi-ant-design:aim-outlined',
|
||||
'vi-ant-design:alert-filled',
|
||||
'vi-ant-design:alert-outlined',
|
||||
'vi-ant-design:alert-twotone',
|
||||
'vi-ant-design:alibaba-outlined',
|
||||
'vi-ant-design:align-center-outlined',
|
||||
'vi-ant-design:align-left-outlined',
|
||||
'vi-ant-design:align-right-outlined',
|
||||
'vi-ant-design:alipay-circle-filled',
|
||||
'vi-ant-design:alipay-circle-outlined',
|
||||
'vi-ant-design:alipay-outlined',
|
||||
'vi-ant-design:alipay-square-filled',
|
||||
'vi-ant-design:aliwangwang-filled',
|
||||
'vi-ant-design:aliwangwang-outlined',
|
||||
'vi-ant-design:aliyun-outlined',
|
||||
'vi-ant-design:amazon-circle-filled',
|
||||
'vi-ant-design:amazon-outlined',
|
||||
'vi-ant-design:amazon-square-filled',
|
||||
'vi-ant-design:android-filled',
|
||||
'vi-ant-design:android-outlined',
|
||||
'vi-ant-design:ant-cloud-outlined',
|
||||
'vi-ant-design:ant-design-outlined',
|
||||
'vi-ant-design:apartment-outlined',
|
||||
'vi-ant-design:api-filled',
|
||||
'vi-ant-design:api-outlined',
|
||||
'vi-ant-design:api-twotone',
|
||||
'vi-ant-design:apple-filled',
|
||||
'vi-ant-design:apple-outlined',
|
||||
'vi-ant-design:appstore-add-outlined',
|
||||
'vi-ant-design:appstore-filled',
|
||||
'vi-ant-design:appstore-outlined',
|
||||
'vi-ant-design:appstore-twotone',
|
||||
'vi-ant-design:area-chart-outlined',
|
||||
'vi-ant-design:arrow-down-outlined',
|
||||
'vi-ant-design:arrow-left-outlined',
|
||||
'vi-ant-design:arrow-right-outlined',
|
||||
'vi-ant-design:arrow-up-outlined',
|
||||
'vi-ant-design:arrows-alt-outlined',
|
||||
'vi-ant-design:audio-filled',
|
||||
'vi-ant-design:audio-muted-outlined',
|
||||
'vi-ant-design:audio-outlined',
|
||||
'vi-ant-design:audio-twotone',
|
||||
'vi-ant-design:audit-outlined',
|
||||
'vi-ant-design:backward-filled',
|
||||
'vi-ant-design:backward-outlined',
|
||||
'vi-ant-design:baidu-outlined',
|
||||
'vi-ant-design:bank-filled',
|
||||
'vi-ant-design:bank-outlined',
|
||||
'vi-ant-design:bank-twotone',
|
||||
'vi-ant-design:bar-chart-outlined',
|
||||
'vi-ant-design:barcode-outlined',
|
||||
'vi-ant-design:bars-outlined',
|
||||
'vi-ant-design:behance-circle-filled',
|
||||
'vi-ant-design:behance-outlined',
|
||||
'vi-ant-design:behance-square-filled',
|
||||
'vi-ant-design:behance-square-outlined',
|
||||
'vi-ant-design:bell-filled',
|
||||
'vi-ant-design:bell-outlined',
|
||||
'vi-ant-design:bell-twotone',
|
||||
'vi-ant-design:bg-colors-outlined',
|
||||
'vi-ant-design:bilibili-filled',
|
||||
'vi-ant-design:bilibili-outlined',
|
||||
'vi-ant-design:block-outlined',
|
||||
'vi-ant-design:bold-outlined',
|
||||
'vi-ant-design:book-filled',
|
||||
'vi-ant-design:book-outlined',
|
||||
'vi-ant-design:book-twotone',
|
||||
'vi-ant-design:border-bottom-outlined',
|
||||
'vi-ant-design:border-horizontal-outlined',
|
||||
'vi-ant-design:border-inner-outlined',
|
||||
'vi-ant-design:border-left-outlined',
|
||||
'vi-ant-design:border-outer-outlined',
|
||||
'vi-ant-design:border-outlined',
|
||||
'vi-ant-design:border-right-outlined',
|
||||
'vi-ant-design:border-top-outlined',
|
||||
'vi-ant-design:border-verticle-outlined',
|
||||
'vi-ant-design:borderless-table-outlined',
|
||||
'vi-ant-design:box-plot-filled',
|
||||
'vi-ant-design:box-plot-outlined',
|
||||
'vi-ant-design:box-plot-twotone',
|
||||
'vi-ant-design:branches-outlined',
|
||||
'vi-ant-design:bug-filled',
|
||||
'vi-ant-design:bug-outlined',
|
||||
'vi-ant-design:bug-twotone',
|
||||
'vi-ant-design:build-filled',
|
||||
'vi-ant-design:build-outlined',
|
||||
'vi-ant-design:build-twotone',
|
||||
'vi-ant-design:bulb-filled',
|
||||
'vi-ant-design:bulb-outlined',
|
||||
'vi-ant-design:bulb-twotone',
|
||||
'vi-ant-design:calculator-filled',
|
||||
'vi-ant-design:calculator-outlined',
|
||||
'vi-ant-design:calculator-twotone',
|
||||
'vi-ant-design:calendar-filled',
|
||||
'vi-ant-design:calendar-outlined',
|
||||
'vi-ant-design:calendar-twotone',
|
||||
'vi-ant-design:camera-filled',
|
||||
'vi-ant-design:camera-outlined',
|
||||
'vi-ant-design:camera-twotone',
|
||||
'vi-ant-design:car-filled',
|
||||
'vi-ant-design:car-outlined',
|
||||
'vi-ant-design:car-twotone',
|
||||
'vi-ant-design:caret-down-filled',
|
||||
'vi-ant-design:caret-down-outlined',
|
||||
'vi-ant-design:caret-left-filled',
|
||||
'vi-ant-design:caret-left-outlined',
|
||||
'vi-ant-design:caret-right-filled',
|
||||
'vi-ant-design:caret-right-outlined',
|
||||
'vi-ant-design:caret-up-filled',
|
||||
'vi-ant-design:caret-up-outlined',
|
||||
'vi-ant-design:carry-out-filled',
|
||||
'vi-ant-design:carry-out-outlined',
|
||||
'vi-ant-design:carry-out-twotone',
|
||||
'vi-ant-design:check-circle-filled',
|
||||
'vi-ant-design:check-circle-outlined',
|
||||
'vi-ant-design:check-circle-twotone',
|
||||
'vi-ant-design:check-outlined',
|
||||
'vi-ant-design:check-square-filled',
|
||||
'vi-ant-design:check-square-outlined',
|
||||
'vi-ant-design:check-square-twotone',
|
||||
'vi-ant-design:chrome-filled',
|
||||
'vi-ant-design:chrome-outlined',
|
||||
'vi-ant-design:ci-circle-filled',
|
||||
'vi-ant-design:ci-circle-outlined',
|
||||
'vi-ant-design:ci-circle-twotone',
|
||||
'vi-ant-design:ci-outlined',
|
||||
'vi-ant-design:ci-twotone',
|
||||
'vi-ant-design:clear-outlined',
|
||||
'vi-ant-design:clock-circle-filled',
|
||||
'vi-ant-design:clock-circle-outlined',
|
||||
'vi-ant-design:clock-circle-twotone',
|
||||
'vi-ant-design:close-circle-filled',
|
||||
'vi-ant-design:close-circle-outlined',
|
||||
'vi-ant-design:close-circle-twotone',
|
||||
'vi-ant-design:close-outlined',
|
||||
'vi-ant-design:close-square-filled',
|
||||
'vi-ant-design:close-square-outlined',
|
||||
'vi-ant-design:close-square-twotone',
|
||||
'vi-ant-design:cloud-download-outlined',
|
||||
'vi-ant-design:cloud-filled',
|
||||
'vi-ant-design:cloud-outlined',
|
||||
'vi-ant-design:cloud-server-outlined',
|
||||
'vi-ant-design:cloud-sync-outlined',
|
||||
'vi-ant-design:cloud-twotone',
|
||||
'vi-ant-design:cloud-upload-outlined',
|
||||
'vi-ant-design:cluster-outlined',
|
||||
'vi-ant-design:code-filled',
|
||||
'vi-ant-design:code-outlined',
|
||||
'vi-ant-design:code-sandbox-circle-filled',
|
||||
'vi-ant-design:code-sandbox-outlined',
|
||||
'vi-ant-design:code-sandbox-square-filled',
|
||||
'vi-ant-design:code-twotone',
|
||||
'vi-ant-design:codepen-circle-filled',
|
||||
'vi-ant-design:codepen-circle-outlined',
|
||||
'vi-ant-design:codepen-outlined',
|
||||
'vi-ant-design:codepen-square-filled',
|
||||
'vi-ant-design:coffee-outlined',
|
||||
'vi-ant-design:column-height-outlined',
|
||||
'vi-ant-design:column-width-outlined',
|
||||
'vi-ant-design:comment-outlined',
|
||||
'vi-ant-design:compass-filled',
|
||||
'vi-ant-design:compass-outlined',
|
||||
'vi-ant-design:compass-twotone',
|
||||
'vi-ant-design:compress-outlined',
|
||||
'vi-ant-design:console-sql-outlined',
|
||||
'vi-ant-design:contacts-filled',
|
||||
'vi-ant-design:contacts-outlined',
|
||||
'vi-ant-design:contacts-twotone',
|
||||
'vi-ant-design:container-filled',
|
||||
'vi-ant-design:container-outlined',
|
||||
'vi-ant-design:container-twotone',
|
||||
'vi-ant-design:control-filled',
|
||||
'vi-ant-design:control-outlined',
|
||||
'vi-ant-design:control-twotone',
|
||||
'vi-ant-design:copy-filled',
|
||||
'vi-ant-design:copy-outlined',
|
||||
'vi-ant-design:copy-twotone',
|
||||
'vi-ant-design:copyright-circle-filled',
|
||||
'vi-ant-design:copyright-circle-outlined',
|
||||
'vi-ant-design:copyright-circle-twotone',
|
||||
'vi-ant-design:copyright-outlined',
|
||||
'vi-ant-design:copyright-twotone',
|
||||
'vi-ant-design:credit-card-filled',
|
||||
'vi-ant-design:credit-card-outlined',
|
||||
'vi-ant-design:credit-card-twotone',
|
||||
'vi-ant-design:crown-filled',
|
||||
'vi-ant-design:crown-outlined',
|
||||
'vi-ant-design:crown-twotone',
|
||||
'vi-ant-design:customer-service-filled',
|
||||
'vi-ant-design:customer-service-outlined',
|
||||
'vi-ant-design:customer-service-twotone',
|
||||
'vi-ant-design:dash-outlined',
|
||||
'vi-ant-design:dashboard-filled',
|
||||
'vi-ant-design:dashboard-outlined',
|
||||
'vi-ant-design:dashboard-twotone',
|
||||
'vi-ant-design:database-filled',
|
||||
'vi-ant-design:database-outlined',
|
||||
'vi-ant-design:database-twotone',
|
||||
'vi-ant-design:delete-column-outlined',
|
||||
'vi-ant-design:delete-filled',
|
||||
'vi-ant-design:delete-outlined',
|
||||
'vi-ant-design:delete-row-outlined',
|
||||
'vi-ant-design:delete-twotone',
|
||||
'vi-ant-design:delivered-procedure-outlined',
|
||||
'vi-ant-design:deployment-unit-outlined',
|
||||
'vi-ant-design:desktop-outlined',
|
||||
'vi-ant-design:diff-filled',
|
||||
'vi-ant-design:diff-outlined',
|
||||
'vi-ant-design:diff-twotone',
|
||||
'vi-ant-design:dingding-outlined',
|
||||
'vi-ant-design:dingtalk-circle-filled',
|
||||
'vi-ant-design:dingtalk-outlined',
|
||||
'vi-ant-design:dingtalk-square-filled',
|
||||
'vi-ant-design:disconnect-outlined',
|
||||
'vi-ant-design:discord-filled',
|
||||
'vi-ant-design:discord-outlined',
|
||||
'vi-ant-design:dislike-filled',
|
||||
'vi-ant-design:dislike-outlined',
|
||||
'vi-ant-design:dislike-twotone',
|
||||
'vi-ant-design:docker-outlined',
|
||||
'vi-ant-design:dollar-circle-filled',
|
||||
'vi-ant-design:dollar-circle-outlined',
|
||||
'vi-ant-design:dollar-circle-twotone',
|
||||
'vi-ant-design:dollar-outlined',
|
||||
'vi-ant-design:dollar-twotone',
|
||||
'vi-ant-design:dot-chart-outlined',
|
||||
'vi-ant-design:dot-net-outlined',
|
||||
'vi-ant-design:double-left-outlined',
|
||||
'vi-ant-design:double-right-outlined',
|
||||
'vi-ant-design:down-circle-filled',
|
||||
'vi-ant-design:down-circle-outlined',
|
||||
'vi-ant-design:down-circle-twotone',
|
||||
'vi-ant-design:down-outlined',
|
||||
'vi-ant-design:down-square-filled',
|
||||
'vi-ant-design:down-square-outlined',
|
||||
'vi-ant-design:down-square-twotone',
|
||||
'vi-ant-design:download-outlined',
|
||||
'vi-ant-design:drag-outlined',
|
||||
'vi-ant-design:dribbble-circle-filled',
|
||||
'vi-ant-design:dribbble-outlined',
|
||||
'vi-ant-design:dribbble-square-filled',
|
||||
'vi-ant-design:dribbble-square-outlined',
|
||||
'vi-ant-design:dropbox-circle-filled',
|
||||
'vi-ant-design:dropbox-outlined',
|
||||
'vi-ant-design:dropbox-square-filled',
|
||||
'vi-ant-design:edit-filled',
|
||||
'vi-ant-design:edit-outlined',
|
||||
'vi-ant-design:edit-twotone',
|
||||
'vi-ant-design:ellipsis-outlined',
|
||||
'vi-ant-design:enter-outlined',
|
||||
'vi-ant-design:environment-filled',
|
||||
'vi-ant-design:environment-outlined',
|
||||
'vi-ant-design:environment-twotone',
|
||||
'vi-ant-design:euro-circle-filled',
|
||||
'vi-ant-design:euro-circle-outlined',
|
||||
'vi-ant-design:euro-circle-twotone',
|
||||
'vi-ant-design:euro-outlined',
|
||||
'vi-ant-design:euro-twotone',
|
||||
'vi-ant-design:exception-outlined',
|
||||
'vi-ant-design:exclamation-circle-filled',
|
||||
'vi-ant-design:exclamation-circle-outlined',
|
||||
'vi-ant-design:exclamation-circle-twotone',
|
||||
'vi-ant-design:exclamation-outlined',
|
||||
'vi-ant-design:expand-alt-outlined',
|
||||
'vi-ant-design:expand-outlined',
|
||||
'vi-ant-design:experiment-filled',
|
||||
'vi-ant-design:experiment-outlined',
|
||||
'vi-ant-design:experiment-twotone',
|
||||
'vi-ant-design:export-outlined',
|
||||
'vi-ant-design:eye-filled',
|
||||
'vi-ant-design:eye-invisible-filled',
|
||||
'vi-ant-design:eye-invisible-outlined',
|
||||
'vi-ant-design:eye-invisible-twotone',
|
||||
'vi-ant-design:eye-outlined',
|
||||
'vi-ant-design:eye-twotone',
|
||||
'vi-ant-design:facebook-filled',
|
||||
'vi-ant-design:facebook-outlined',
|
||||
'vi-ant-design:fall-outlined',
|
||||
'vi-ant-design:fast-backward-filled',
|
||||
'vi-ant-design:fast-backward-outlined',
|
||||
'vi-ant-design:fast-forward-filled',
|
||||
'vi-ant-design:fast-forward-outlined',
|
||||
'vi-ant-design:field-binary-outlined',
|
||||
'vi-ant-design:field-number-outlined',
|
||||
'vi-ant-design:field-string-outlined',
|
||||
'vi-ant-design:field-time-outlined',
|
||||
'vi-ant-design:file-add-filled',
|
||||
'vi-ant-design:file-add-outlined',
|
||||
'vi-ant-design:file-add-twotone',
|
||||
'vi-ant-design:file-done-outlined',
|
||||
'vi-ant-design:file-excel-filled',
|
||||
'vi-ant-design:file-excel-outlined',
|
||||
'vi-ant-design:file-excel-twotone',
|
||||
'vi-ant-design:file-exclamation-filled',
|
||||
'vi-ant-design:file-exclamation-outlined',
|
||||
'vi-ant-design:file-exclamation-twotone',
|
||||
'vi-ant-design:file-filled',
|
||||
'vi-ant-design:file-gif-outlined',
|
||||
'vi-ant-design:file-image-filled',
|
||||
'vi-ant-design:file-image-outlined',
|
||||
'vi-ant-design:file-image-twotone',
|
||||
'vi-ant-design:file-jpg-outlined',
|
||||
'vi-ant-design:file-markdown-filled',
|
||||
'vi-ant-design:file-markdown-outlined',
|
||||
'vi-ant-design:file-markdown-twotone',
|
||||
'vi-ant-design:file-outlined',
|
||||
'vi-ant-design:file-pdf-filled',
|
||||
'vi-ant-design:file-pdf-outlined',
|
||||
'vi-ant-design:file-pdf-twotone',
|
||||
'vi-ant-design:file-ppt-filled',
|
||||
'vi-ant-design:file-ppt-outlined',
|
||||
'vi-ant-design:file-ppt-twotone',
|
||||
'vi-ant-design:file-protect-outlined',
|
||||
'vi-ant-design:file-search-outlined',
|
||||
'vi-ant-design:file-sync-outlined',
|
||||
'vi-ant-design:file-text-filled',
|
||||
'vi-ant-design:file-text-outlined',
|
||||
'vi-ant-design:file-text-twotone',
|
||||
'vi-ant-design:file-twotone',
|
||||
'vi-ant-design:file-unknown-filled',
|
||||
'vi-ant-design:file-unknown-outlined',
|
||||
'vi-ant-design:file-unknown-twotone',
|
||||
'vi-ant-design:file-word-filled',
|
||||
'vi-ant-design:file-word-outlined',
|
||||
'vi-ant-design:file-word-twotone',
|
||||
'vi-ant-design:file-zip-filled',
|
||||
'vi-ant-design:file-zip-outlined',
|
||||
'vi-ant-design:file-zip-twotone',
|
||||
'vi-ant-design:filter-filled',
|
||||
'vi-ant-design:filter-outlined',
|
||||
'vi-ant-design:filter-twotone',
|
||||
'vi-ant-design:fire-filled',
|
||||
'vi-ant-design:fire-outlined',
|
||||
'vi-ant-design:fire-twotone',
|
||||
'vi-ant-design:flag-filled',
|
||||
'vi-ant-design:flag-outlined',
|
||||
'vi-ant-design:flag-twotone',
|
||||
'vi-ant-design:folder-add-filled',
|
||||
'vi-ant-design:folder-add-outlined',
|
||||
'vi-ant-design:folder-add-twotone',
|
||||
'vi-ant-design:folder-filled',
|
||||
'vi-ant-design:folder-open-filled',
|
||||
'vi-ant-design:folder-open-outlined',
|
||||
'vi-ant-design:folder-open-twotone',
|
||||
'vi-ant-design:folder-outlined',
|
||||
'vi-ant-design:folder-twotone',
|
||||
'vi-ant-design:folder-view-outlined',
|
||||
'vi-ant-design:font-colors-outlined',
|
||||
'vi-ant-design:font-size-outlined',
|
||||
'vi-ant-design:fork-outlined',
|
||||
'vi-ant-design:form-outlined',
|
||||
'vi-ant-design:format-painter-filled',
|
||||
'vi-ant-design:format-painter-outlined',
|
||||
'vi-ant-design:forward-filled',
|
||||
'vi-ant-design:forward-outlined',
|
||||
'vi-ant-design:frown-filled',
|
||||
'vi-ant-design:frown-outlined',
|
||||
'vi-ant-design:frown-twotone',
|
||||
'vi-ant-design:fullscreen-exit-outlined',
|
||||
'vi-ant-design:fullscreen-outlined',
|
||||
'vi-ant-design:function-outlined',
|
||||
'vi-ant-design:fund-filled',
|
||||
'vi-ant-design:fund-outlined',
|
||||
'vi-ant-design:fund-projection-screen-outlined',
|
||||
'vi-ant-design:fund-twotone',
|
||||
'vi-ant-design:fund-view-outlined',
|
||||
'vi-ant-design:funnel-plot-filled',
|
||||
'vi-ant-design:funnel-plot-outlined',
|
||||
'vi-ant-design:funnel-plot-twotone',
|
||||
'vi-ant-design:gateway-outlined',
|
||||
'vi-ant-design:gif-outlined',
|
||||
'vi-ant-design:gift-filled',
|
||||
'vi-ant-design:gift-outlined',
|
||||
'vi-ant-design:gift-twotone',
|
||||
'vi-ant-design:github-filled',
|
||||
'vi-ant-design:github-outlined',
|
||||
'vi-ant-design:gitlab-filled',
|
||||
'vi-ant-design:gitlab-outlined',
|
||||
'vi-ant-design:global-outlined',
|
||||
'vi-ant-design:gold-filled',
|
||||
'vi-ant-design:gold-outlined',
|
||||
'vi-ant-design:gold-twotone',
|
||||
'vi-ant-design:golden-filled',
|
||||
'vi-ant-design:google-circle-filled',
|
||||
'vi-ant-design:google-outlined',
|
||||
'vi-ant-design:google-plus-circle-filled',
|
||||
'vi-ant-design:google-plus-outlined',
|
||||
'vi-ant-design:google-plus-square-filled',
|
||||
'vi-ant-design:google-square-filled',
|
||||
'vi-ant-design:group-outlined',
|
||||
'vi-ant-design:harmony-o-s-outlined',
|
||||
'vi-ant-design:hdd-filled',
|
||||
'vi-ant-design:hdd-outlined',
|
||||
'vi-ant-design:hdd-twotone',
|
||||
'vi-ant-design:heart-filled',
|
||||
'vi-ant-design:heart-outlined',
|
||||
'vi-ant-design:heart-twotone',
|
||||
'vi-ant-design:heat-map-outlined',
|
||||
'vi-ant-design:highlight-filled',
|
||||
'vi-ant-design:highlight-outlined',
|
||||
'vi-ant-design:highlight-twotone',
|
||||
'vi-ant-design:history-outlined',
|
||||
'vi-ant-design:holder-outlined',
|
||||
'vi-ant-design:home-filled',
|
||||
'vi-ant-design:home-outlined',
|
||||
'vi-ant-design:home-twotone',
|
||||
'vi-ant-design:hourglass-filled',
|
||||
'vi-ant-design:hourglass-outlined',
|
||||
'vi-ant-design:hourglass-twotone',
|
||||
'vi-ant-design:html5-filled',
|
||||
'vi-ant-design:html5-outlined',
|
||||
'vi-ant-design:html5-twotone',
|
||||
'vi-ant-design:idcard-filled',
|
||||
'vi-ant-design:idcard-outlined',
|
||||
'vi-ant-design:idcard-twotone',
|
||||
'vi-ant-design:ie-circle-filled',
|
||||
'vi-ant-design:ie-outlined',
|
||||
'vi-ant-design:ie-square-filled',
|
||||
'vi-ant-design:import-outlined',
|
||||
'vi-ant-design:inbox-outlined',
|
||||
'vi-ant-design:info-circle-filled',
|
||||
'vi-ant-design:info-circle-outlined',
|
||||
'vi-ant-design:info-circle-twotone',
|
||||
'vi-ant-design:info-outlined',
|
||||
'vi-ant-design:insert-row-above-outlined',
|
||||
'vi-ant-design:insert-row-below-outlined',
|
||||
'vi-ant-design:insert-row-left-outlined',
|
||||
'vi-ant-design:insert-row-right-outlined',
|
||||
'vi-ant-design:instagram-filled',
|
||||
'vi-ant-design:instagram-outlined',
|
||||
'vi-ant-design:insurance-filled',
|
||||
'vi-ant-design:insurance-outlined',
|
||||
'vi-ant-design:insurance-twotone',
|
||||
'vi-ant-design:interaction-filled',
|
||||
'vi-ant-design:interaction-outlined',
|
||||
'vi-ant-design:interaction-twotone',
|
||||
'vi-ant-design:issues-close-outlined',
|
||||
'vi-ant-design:italic-outlined',
|
||||
'vi-ant-design:java-outlined',
|
||||
'vi-ant-design:java-script-outlined',
|
||||
'vi-ant-design:key-outlined',
|
||||
'vi-ant-design:kubernetes-outlined',
|
||||
'vi-ant-design:laptop-outlined',
|
||||
'vi-ant-design:layout-filled',
|
||||
'vi-ant-design:layout-outlined',
|
||||
'vi-ant-design:layout-twotone',
|
||||
'vi-ant-design:left-circle-filled',
|
||||
'vi-ant-design:left-circle-outlined',
|
||||
'vi-ant-design:left-circle-twotone',
|
||||
'vi-ant-design:left-outlined',
|
||||
'vi-ant-design:left-square-filled',
|
||||
'vi-ant-design:left-square-outlined',
|
||||
'vi-ant-design:left-square-twotone',
|
||||
'vi-ant-design:like-filled',
|
||||
'vi-ant-design:like-outlined',
|
||||
'vi-ant-design:like-twotone',
|
||||
'vi-ant-design:line-chart-outlined',
|
||||
'vi-ant-design:line-height-outlined',
|
||||
'vi-ant-design:line-outlined',
|
||||
'vi-ant-design:link-outlined',
|
||||
'vi-ant-design:linkedin-filled',
|
||||
'vi-ant-design:linkedin-outlined',
|
||||
'vi-ant-design:linux-outlined',
|
||||
'vi-ant-design:loading-3-quarters-outlined',
|
||||
'vi-ant-design:loading-outlined',
|
||||
'vi-ant-design:lock-filled',
|
||||
'vi-ant-design:lock-outlined',
|
||||
'vi-ant-design:lock-twotone',
|
||||
'vi-ant-design:login-outlined',
|
||||
'vi-ant-design:logout-outlined',
|
||||
'vi-ant-design:mac-command-filled',
|
||||
'vi-ant-design:mac-command-outlined',
|
||||
'vi-ant-design:mail-filled',
|
||||
'vi-ant-design:mail-outlined',
|
||||
'vi-ant-design:mail-twotone',
|
||||
'vi-ant-design:man-outlined',
|
||||
'vi-ant-design:medicine-box-filled',
|
||||
'vi-ant-design:medicine-box-outlined',
|
||||
'vi-ant-design:medicine-box-twotone',
|
||||
'vi-ant-design:medium-circle-filled',
|
||||
'vi-ant-design:medium-outlined',
|
||||
'vi-ant-design:medium-square-filled',
|
||||
'vi-ant-design:medium-workmark-outlined',
|
||||
'vi-ant-design:meh-filled',
|
||||
'vi-ant-design:meh-outlined',
|
||||
'vi-ant-design:meh-twotone',
|
||||
'vi-ant-design:menu-fold-outlined',
|
||||
'vi-ant-design:menu-outlined',
|
||||
'vi-ant-design:menu-unfold-outlined',
|
||||
'vi-ant-design:merge-cells-outlined',
|
||||
'vi-ant-design:merge-filled',
|
||||
'vi-ant-design:merge-outlined',
|
||||
'vi-ant-design:message-filled',
|
||||
'vi-ant-design:message-outlined',
|
||||
'vi-ant-design:message-twotone',
|
||||
'vi-ant-design:minus-circle-filled',
|
||||
'vi-ant-design:minus-circle-outlined',
|
||||
'vi-ant-design:minus-circle-twotone',
|
||||
'vi-ant-design:minus-outlined',
|
||||
'vi-ant-design:minus-square-filled',
|
||||
'vi-ant-design:minus-square-outlined',
|
||||
'vi-ant-design:minus-square-twotone',
|
||||
'vi-ant-design:mobile-filled',
|
||||
'vi-ant-design:mobile-outlined',
|
||||
'vi-ant-design:mobile-twotone',
|
||||
'vi-ant-design:money-collect-filled',
|
||||
'vi-ant-design:money-collect-outlined',
|
||||
'vi-ant-design:money-collect-twotone',
|
||||
'vi-ant-design:monitor-outlined',
|
||||
'vi-ant-design:moon-filled',
|
||||
'vi-ant-design:moon-outlined',
|
||||
'vi-ant-design:more-outlined',
|
||||
'vi-ant-design:muted-filled',
|
||||
'vi-ant-design:muted-outlined',
|
||||
'vi-ant-design:node-collapse-outlined',
|
||||
'vi-ant-design:node-expand-outlined',
|
||||
'vi-ant-design:node-index-outlined',
|
||||
'vi-ant-design:notification-filled',
|
||||
'vi-ant-design:notification-outlined',
|
||||
'vi-ant-design:notification-twotone',
|
||||
'vi-ant-design:number-outlined',
|
||||
'vi-ant-design:one-to-one-outlined',
|
||||
'vi-ant-design:open-a-i-filled',
|
||||
'vi-ant-design:open-a-i-outlined',
|
||||
'vi-ant-design:ordered-list-outlined',
|
||||
'vi-ant-design:paper-clip-outlined',
|
||||
'vi-ant-design:partition-outlined',
|
||||
'vi-ant-design:pause-circle-filled',
|
||||
'vi-ant-design:pause-circle-outlined',
|
||||
'vi-ant-design:pause-circle-twotone',
|
||||
'vi-ant-design:pause-outlined',
|
||||
'vi-ant-design:pay-circle-filled',
|
||||
'vi-ant-design:pay-circle-outlined',
|
||||
'vi-ant-design:percentage-outlined',
|
||||
'vi-ant-design:phone-filled',
|
||||
'vi-ant-design:phone-outlined',
|
||||
'vi-ant-design:phone-twotone',
|
||||
'vi-ant-design:pic-center-outlined',
|
||||
'vi-ant-design:pic-left-outlined',
|
||||
'vi-ant-design:pic-right-outlined',
|
||||
'vi-ant-design:picture-filled',
|
||||
'vi-ant-design:picture-outlined',
|
||||
'vi-ant-design:picture-twotone',
|
||||
'vi-ant-design:pie-chart-filled',
|
||||
'vi-ant-design:pie-chart-outlined',
|
||||
'vi-ant-design:pie-chart-twotone',
|
||||
'vi-ant-design:pinterest-filled',
|
||||
'vi-ant-design:pinterest-outlined',
|
||||
'vi-ant-design:play-circle-filled',
|
||||
'vi-ant-design:play-circle-outlined',
|
||||
'vi-ant-design:play-circle-twotone',
|
||||
'vi-ant-design:play-square-filled',
|
||||
'vi-ant-design:play-square-outlined',
|
||||
'vi-ant-design:play-square-twotone',
|
||||
'vi-ant-design:plus-circle-filled',
|
||||
'vi-ant-design:plus-circle-outlined',
|
||||
'vi-ant-design:plus-circle-twotone',
|
||||
'vi-ant-design:plus-outlined',
|
||||
'vi-ant-design:plus-square-filled',
|
||||
'vi-ant-design:plus-square-outlined',
|
||||
'vi-ant-design:plus-square-twotone',
|
||||
'vi-ant-design:pound-circle-filled',
|
||||
'vi-ant-design:pound-circle-outlined',
|
||||
'vi-ant-design:pound-circle-twotone',
|
||||
'vi-ant-design:pound-outlined',
|
||||
'vi-ant-design:poweroff-outlined',
|
||||
'vi-ant-design:printer-filled',
|
||||
'vi-ant-design:printer-outlined',
|
||||
'vi-ant-design:printer-twotone',
|
||||
'vi-ant-design:product-filled',
|
||||
'vi-ant-design:product-outlined',
|
||||
'vi-ant-design:profile-filled',
|
||||
'vi-ant-design:profile-outlined',
|
||||
'vi-ant-design:profile-twotone',
|
||||
'vi-ant-design:project-filled',
|
||||
'vi-ant-design:project-outlined',
|
||||
'vi-ant-design:project-twotone',
|
||||
'vi-ant-design:property-safety-filled',
|
||||
'vi-ant-design:property-safety-outlined',
|
||||
'vi-ant-design:property-safety-twotone',
|
||||
'vi-ant-design:pull-request-outlined',
|
||||
'vi-ant-design:pushpin-filled',
|
||||
'vi-ant-design:pushpin-outlined',
|
||||
'vi-ant-design:pushpin-twotone',
|
||||
'vi-ant-design:python-outlined',
|
||||
'vi-ant-design:qq-circle-filled',
|
||||
'vi-ant-design:qq-outlined',
|
||||
'vi-ant-design:qq-square-filled',
|
||||
'vi-ant-design:qrcode-outlined',
|
||||
'vi-ant-design:question-circle-filled',
|
||||
'vi-ant-design:question-circle-outlined',
|
||||
'vi-ant-design:question-circle-twotone',
|
||||
'vi-ant-design:question-outlined',
|
||||
'vi-ant-design:radar-chart-outlined',
|
||||
'vi-ant-design:radius-bottomleft-outlined',
|
||||
'vi-ant-design:radius-bottomright-outlined',
|
||||
'vi-ant-design:radius-setting-outlined',
|
||||
'vi-ant-design:radius-upleft-outlined',
|
||||
'vi-ant-design:radius-upright-outlined',
|
||||
'vi-ant-design:read-filled',
|
||||
'vi-ant-design:read-outlined',
|
||||
'vi-ant-design:reconciliation-filled',
|
||||
'vi-ant-design:reconciliation-outlined',
|
||||
'vi-ant-design:reconciliation-twotone',
|
||||
'vi-ant-design:red-envelope-filled',
|
||||
'vi-ant-design:red-envelope-outlined',
|
||||
'vi-ant-design:red-envelope-twotone',
|
||||
'vi-ant-design:reddit-circle-filled',
|
||||
'vi-ant-design:reddit-outlined',
|
||||
'vi-ant-design:reddit-square-filled',
|
||||
'vi-ant-design:redo-outlined',
|
||||
'vi-ant-design:reload-outlined',
|
||||
'vi-ant-design:rest-filled',
|
||||
'vi-ant-design:rest-outlined',
|
||||
'vi-ant-design:rest-twotone',
|
||||
'vi-ant-design:retweet-outlined',
|
||||
'vi-ant-design:right-circle-filled',
|
||||
'vi-ant-design:right-circle-outlined',
|
||||
'vi-ant-design:right-circle-twotone',
|
||||
'vi-ant-design:right-outlined',
|
||||
'vi-ant-design:right-square-filled',
|
||||
'vi-ant-design:right-square-outlined',
|
||||
'vi-ant-design:right-square-twotone',
|
||||
'vi-ant-design:rise-outlined',
|
||||
'vi-ant-design:robot-filled',
|
||||
'vi-ant-design:robot-outlined',
|
||||
'vi-ant-design:rocket-filled',
|
||||
'vi-ant-design:rocket-outlined',
|
||||
'vi-ant-design:rocket-twotone',
|
||||
'vi-ant-design:rollback-outlined',
|
||||
'vi-ant-design:rotate-left-outlined',
|
||||
'vi-ant-design:rotate-right-outlined',
|
||||
'vi-ant-design:ruby-outlined',
|
||||
'vi-ant-design:safety-certificate-filled',
|
||||
'vi-ant-design:safety-certificate-outlined',
|
||||
'vi-ant-design:safety-certificate-twotone',
|
||||
'vi-ant-design:safety-outlined',
|
||||
'vi-ant-design:save-filled',
|
||||
'vi-ant-design:save-outlined',
|
||||
'vi-ant-design:save-twotone',
|
||||
'vi-ant-design:scan-outlined',
|
||||
'vi-ant-design:schedule-filled',
|
||||
'vi-ant-design:schedule-outlined',
|
||||
'vi-ant-design:schedule-twotone',
|
||||
'vi-ant-design:scissor-outlined',
|
||||
'vi-ant-design:search-outlined',
|
||||
'vi-ant-design:security-scan-filled',
|
||||
'vi-ant-design:security-scan-outlined',
|
||||
'vi-ant-design:security-scan-twotone',
|
||||
'vi-ant-design:select-outlined',
|
||||
'vi-ant-design:send-outlined',
|
||||
'vi-ant-design:setting-filled',
|
||||
'vi-ant-design:setting-outlined',
|
||||
'vi-ant-design:setting-twotone',
|
||||
'vi-ant-design:shake-outlined',
|
||||
'vi-ant-design:share-alt-outlined',
|
||||
'vi-ant-design:shop-filled',
|
||||
'vi-ant-design:shop-outlined',
|
||||
'vi-ant-design:shop-twotone',
|
||||
'vi-ant-design:shopping-cart-outlined',
|
||||
'vi-ant-design:shopping-filled',
|
||||
'vi-ant-design:shopping-outlined',
|
||||
'vi-ant-design:shopping-twotone',
|
||||
'vi-ant-design:shrink-outlined',
|
||||
'vi-ant-design:signal-filled',
|
||||
'vi-ant-design:signature-filled',
|
||||
'vi-ant-design:signature-outlined',
|
||||
'vi-ant-design:sisternode-outlined',
|
||||
'vi-ant-design:sketch-circle-filled',
|
||||
'vi-ant-design:sketch-outlined',
|
||||
'vi-ant-design:sketch-square-filled',
|
||||
'vi-ant-design:skin-filled',
|
||||
'vi-ant-design:skin-outlined',
|
||||
'vi-ant-design:skin-twotone',
|
||||
'vi-ant-design:skype-filled',
|
||||
'vi-ant-design:skype-outlined',
|
||||
'vi-ant-design:slack-circle-filled',
|
||||
'vi-ant-design:slack-outlined',
|
||||
'vi-ant-design:slack-square-filled',
|
||||
'vi-ant-design:slack-square-outlined',
|
||||
'vi-ant-design:sliders-filled',
|
||||
'vi-ant-design:sliders-outlined',
|
||||
'vi-ant-design:sliders-twotone',
|
||||
'vi-ant-design:small-dash-outlined',
|
||||
'vi-ant-design:smile-filled',
|
||||
'vi-ant-design:smile-outlined',
|
||||
'vi-ant-design:smile-twotone',
|
||||
'vi-ant-design:snippets-filled',
|
||||
'vi-ant-design:snippets-outlined',
|
||||
'vi-ant-design:snippets-twotone',
|
||||
'vi-ant-design:solution-outlined',
|
||||
'vi-ant-design:sort-ascending-outlined',
|
||||
'vi-ant-design:sort-descending-outlined',
|
||||
'vi-ant-design:sound-filled',
|
||||
'vi-ant-design:sound-outlined',
|
||||
'vi-ant-design:sound-twotone',
|
||||
'vi-ant-design:split-cells-outlined',
|
||||
'vi-ant-design:spotify-filled',
|
||||
'vi-ant-design:spotify-outlined',
|
||||
'vi-ant-design:star-filled',
|
||||
'vi-ant-design:star-outlined',
|
||||
'vi-ant-design:star-twotone',
|
||||
'vi-ant-design:step-backward-filled',
|
||||
'vi-ant-design:step-backward-outlined',
|
||||
'vi-ant-design:step-forward-filled',
|
||||
'vi-ant-design:step-forward-outlined',
|
||||
'vi-ant-design:stock-outlined',
|
||||
'vi-ant-design:stop-filled',
|
||||
'vi-ant-design:stop-outlined',
|
||||
'vi-ant-design:stop-twotone',
|
||||
'vi-ant-design:strikethrough-outlined',
|
||||
'vi-ant-design:subnode-outlined',
|
||||
'vi-ant-design:sun-filled',
|
||||
'vi-ant-design:sun-outlined',
|
||||
'vi-ant-design:swap-left-outlined',
|
||||
'vi-ant-design:swap-outlined',
|
||||
'vi-ant-design:swap-right-outlined',
|
||||
'vi-ant-design:switcher-filled',
|
||||
'vi-ant-design:switcher-outlined',
|
||||
'vi-ant-design:switcher-twotone',
|
||||
'vi-ant-design:sync-outlined',
|
||||
'vi-ant-design:table-outlined',
|
||||
'vi-ant-design:tablet-filled',
|
||||
'vi-ant-design:tablet-outlined',
|
||||
'vi-ant-design:tablet-twotone',
|
||||
'vi-ant-design:tag-filled',
|
||||
'vi-ant-design:tag-outlined',
|
||||
'vi-ant-design:tag-twotone',
|
||||
'vi-ant-design:tags-filled',
|
||||
'vi-ant-design:tags-outlined',
|
||||
'vi-ant-design:tags-twotone',
|
||||
'vi-ant-design:taobao-circle-filled',
|
||||
'vi-ant-design:taobao-circle-outlined',
|
||||
'vi-ant-design:taobao-outlined',
|
||||
'vi-ant-design:taobao-square-filled',
|
||||
'vi-ant-design:team-outlined',
|
||||
'vi-ant-design:thunderbolt-filled',
|
||||
'vi-ant-design:thunderbolt-outlined',
|
||||
'vi-ant-design:thunderbolt-twotone',
|
||||
'vi-ant-design:tik-tok-filled',
|
||||
'vi-ant-design:tik-tok-outlined',
|
||||
'vi-ant-design:to-top-outlined',
|
||||
'vi-ant-design:tool-filled',
|
||||
'vi-ant-design:tool-outlined',
|
||||
'vi-ant-design:tool-twotone',
|
||||
'vi-ant-design:trademark-circle-filled',
|
||||
'vi-ant-design:trademark-circle-outlined',
|
||||
'vi-ant-design:trademark-circle-twotone',
|
||||
'vi-ant-design:trademark-outlined',
|
||||
'vi-ant-design:transaction-outlined',
|
||||
'vi-ant-design:translation-outlined',
|
||||
'vi-ant-design:trophy-filled',
|
||||
'vi-ant-design:trophy-outlined',
|
||||
'vi-ant-design:trophy-twotone',
|
||||
'vi-ant-design:truck-filled',
|
||||
'vi-ant-design:truck-outlined',
|
||||
'vi-ant-design:twitch-outlined',
|
||||
'vi-ant-design:twitter-circle-filled',
|
||||
'vi-ant-design:twitter-outlined',
|
||||
'vi-ant-design:twitter-square-filled',
|
||||
'vi-ant-design:underline-outlined',
|
||||
'vi-ant-design:undo-outlined',
|
||||
'vi-ant-design:ungroup-outlined',
|
||||
'vi-ant-design:unlock-filled',
|
||||
'vi-ant-design:unlock-outlined',
|
||||
'vi-ant-design:unlock-twotone',
|
||||
'vi-ant-design:unordered-list-outlined',
|
||||
'vi-ant-design:up-circle-filled',
|
||||
'vi-ant-design:up-circle-outlined',
|
||||
'vi-ant-design:up-circle-twotone',
|
||||
'vi-ant-design:up-outlined',
|
||||
'vi-ant-design:up-square-filled',
|
||||
'vi-ant-design:up-square-outlined',
|
||||
'vi-ant-design:up-square-twotone',
|
||||
'vi-ant-design:upload-outlined',
|
||||
'vi-ant-design:usb-filled',
|
||||
'vi-ant-design:usb-outlined',
|
||||
'vi-ant-design:usb-twotone',
|
||||
'vi-ant-design:user-add-outlined',
|
||||
'vi-ant-design:user-delete-outlined',
|
||||
'vi-ant-design:user-outlined',
|
||||
'vi-ant-design:user-switch-outlined',
|
||||
'vi-ant-design:usergroup-add-outlined',
|
||||
'vi-ant-design:usergroup-delete-outlined',
|
||||
'vi-ant-design:verified-outlined',
|
||||
'vi-ant-design:vertical-align-bottom-outlined',
|
||||
'vi-ant-design:vertical-align-middle-outlined',
|
||||
'vi-ant-design:vertical-align-top-outlined',
|
||||
'vi-ant-design:vertical-left-outlined',
|
||||
'vi-ant-design:vertical-right-outlined',
|
||||
'vi-ant-design:video-camera-add-outlined',
|
||||
'vi-ant-design:video-camera-filled',
|
||||
'vi-ant-design:video-camera-outlined',
|
||||
'vi-ant-design:video-camera-twotone',
|
||||
'vi-ant-design:wallet-filled',
|
||||
'vi-ant-design:wallet-outlined',
|
||||
'vi-ant-design:wallet-twotone',
|
||||
'vi-ant-design:warning-filled',
|
||||
'vi-ant-design:warning-outlined',
|
||||
'vi-ant-design:warning-twotone',
|
||||
'vi-ant-design:wechat-filled',
|
||||
'vi-ant-design:wechat-outlined',
|
||||
'vi-ant-design:wechat-work-filled',
|
||||
'vi-ant-design:wechat-work-outlined',
|
||||
'vi-ant-design:weibo-circle-filled',
|
||||
'vi-ant-design:weibo-circle-outlined',
|
||||
'vi-ant-design:weibo-outlined',
|
||||
'vi-ant-design:weibo-square-filled',
|
||||
'vi-ant-design:weibo-square-outlined',
|
||||
'vi-ant-design:whats-app-outlined',
|
||||
'vi-ant-design:wifi-outlined',
|
||||
'vi-ant-design:windows-filled',
|
||||
'vi-ant-design:windows-outlined',
|
||||
'vi-ant-design:woman-outlined',
|
||||
'vi-ant-design:x-filled',
|
||||
'vi-ant-design:x-outlined',
|
||||
'vi-ant-design:yahoo-filled',
|
||||
'vi-ant-design:yahoo-outlined',
|
||||
'vi-ant-design:youtube-filled',
|
||||
'vi-ant-design:youtube-outlined',
|
||||
'vi-ant-design:yuque-filled',
|
||||
'vi-ant-design:yuque-outlined',
|
||||
'vi-ant-design:zhihu-circle-filled',
|
||||
'vi-ant-design:zhihu-outlined',
|
||||
'vi-ant-design:zhihu-square-filled',
|
||||
'vi-ant-design:zoom-in-outlined',
|
||||
'vi-ant-design:zoom-out-outlined'
|
||||
]
|
||||
}
|
||||
299
promo-ui2/src/components/IconPicker/src/data/icons.ep.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
export default {
|
||||
name: 'Element Plus',
|
||||
prefix: 'vi-ep',
|
||||
icons: [
|
||||
'vi-ep:add-location',
|
||||
'vi-ep:aim',
|
||||
'vi-ep:alarm-clock',
|
||||
'vi-ep:apple',
|
||||
'vi-ep:arrow-down',
|
||||
'vi-ep:arrow-down-bold',
|
||||
'vi-ep:arrow-left',
|
||||
'vi-ep:arrow-left-bold',
|
||||
'vi-ep:arrow-right',
|
||||
'vi-ep:arrow-right-bold',
|
||||
'vi-ep:arrow-up',
|
||||
'vi-ep:arrow-up-bold',
|
||||
'vi-ep:avatar',
|
||||
'vi-ep:back',
|
||||
'vi-ep:baseball',
|
||||
'vi-ep:basketball',
|
||||
'vi-ep:bell',
|
||||
'vi-ep:bell-filled',
|
||||
'vi-ep:bicycle',
|
||||
'vi-ep:bottom',
|
||||
'vi-ep:bottom-left',
|
||||
'vi-ep:bottom-right',
|
||||
'vi-ep:bowl',
|
||||
'vi-ep:box',
|
||||
'vi-ep:briefcase',
|
||||
'vi-ep:brush',
|
||||
'vi-ep:brush-filled',
|
||||
'vi-ep:burger',
|
||||
'vi-ep:calendar',
|
||||
'vi-ep:camera',
|
||||
'vi-ep:camera-filled',
|
||||
'vi-ep:caret-bottom',
|
||||
'vi-ep:caret-left',
|
||||
'vi-ep:caret-right',
|
||||
'vi-ep:caret-top',
|
||||
'vi-ep:cellphone',
|
||||
'vi-ep:chat-dot-round',
|
||||
'vi-ep:chat-dot-square',
|
||||
'vi-ep:chat-line-round',
|
||||
'vi-ep:chat-line-square',
|
||||
'vi-ep:chat-round',
|
||||
'vi-ep:chat-square',
|
||||
'vi-ep:check',
|
||||
'vi-ep:checked',
|
||||
'vi-ep:cherry',
|
||||
'vi-ep:chicken',
|
||||
'vi-ep:chrome-filled',
|
||||
'vi-ep:circle-check',
|
||||
'vi-ep:circle-check-filled',
|
||||
'vi-ep:circle-close',
|
||||
'vi-ep:circle-close-filled',
|
||||
'vi-ep:circle-plus',
|
||||
'vi-ep:circle-plus-filled',
|
||||
'vi-ep:clock',
|
||||
'vi-ep:close',
|
||||
'vi-ep:close-bold',
|
||||
'vi-ep:cloudy',
|
||||
'vi-ep:coffee',
|
||||
'vi-ep:coffee-cup',
|
||||
'vi-ep:coin',
|
||||
'vi-ep:cold-drink',
|
||||
'vi-ep:collection',
|
||||
'vi-ep:collection-tag',
|
||||
'vi-ep:comment',
|
||||
'vi-ep:compass',
|
||||
'vi-ep:connection',
|
||||
'vi-ep:coordinate',
|
||||
'vi-ep:copy-document',
|
||||
'vi-ep:cpu',
|
||||
'vi-ep:credit-card',
|
||||
'vi-ep:crop',
|
||||
'vi-ep:d-arrow-left',
|
||||
'vi-ep:d-arrow-right',
|
||||
'vi-ep:d-caret',
|
||||
'vi-ep:data-analysis',
|
||||
'vi-ep:data-board',
|
||||
'vi-ep:data-line',
|
||||
'vi-ep:delete',
|
||||
'vi-ep:delete-filled',
|
||||
'vi-ep:delete-location',
|
||||
'vi-ep:dessert',
|
||||
'vi-ep:discount',
|
||||
'vi-ep:dish',
|
||||
'vi-ep:dish-dot',
|
||||
'vi-ep:document',
|
||||
'vi-ep:document-add',
|
||||
'vi-ep:document-checked',
|
||||
'vi-ep:document-copy',
|
||||
'vi-ep:document-delete',
|
||||
'vi-ep:document-remove',
|
||||
'vi-ep:download',
|
||||
'vi-ep:drizzling',
|
||||
'vi-ep:edit',
|
||||
'vi-ep:edit-pen',
|
||||
'vi-ep:eleme',
|
||||
'vi-ep:eleme-filled',
|
||||
'vi-ep:element-plus',
|
||||
'vi-ep:expand',
|
||||
'vi-ep:failed',
|
||||
'vi-ep:female',
|
||||
'vi-ep:files',
|
||||
'vi-ep:film',
|
||||
'vi-ep:filter',
|
||||
'vi-ep:finished',
|
||||
'vi-ep:first-aid-kit',
|
||||
'vi-ep:flag',
|
||||
'vi-ep:fold',
|
||||
'vi-ep:folder',
|
||||
'vi-ep:folder-add',
|
||||
'vi-ep:folder-checked',
|
||||
'vi-ep:folder-delete',
|
||||
'vi-ep:folder-opened',
|
||||
'vi-ep:folder-remove',
|
||||
'vi-ep:food',
|
||||
'vi-ep:football',
|
||||
'vi-ep:fork-spoon',
|
||||
'vi-ep:fries',
|
||||
'vi-ep:full-screen',
|
||||
'vi-ep:goblet',
|
||||
'vi-ep:goblet-full',
|
||||
'vi-ep:goblet-square',
|
||||
'vi-ep:goblet-square-full',
|
||||
'vi-ep:gold-medal',
|
||||
'vi-ep:goods',
|
||||
'vi-ep:goods-filled',
|
||||
'vi-ep:grape',
|
||||
'vi-ep:grid',
|
||||
'vi-ep:guide',
|
||||
'vi-ep:handbag',
|
||||
'vi-ep:headset',
|
||||
'vi-ep:help',
|
||||
'vi-ep:help-filled',
|
||||
'vi-ep:hide',
|
||||
'vi-ep:histogram',
|
||||
'vi-ep:home-filled',
|
||||
'vi-ep:hot-water',
|
||||
'vi-ep:house',
|
||||
'vi-ep:ice-cream',
|
||||
'vi-ep:ice-cream-round',
|
||||
'vi-ep:ice-cream-square',
|
||||
'vi-ep:ice-drink',
|
||||
'vi-ep:ice-tea',
|
||||
'vi-ep:info-filled',
|
||||
'vi-ep:iphone',
|
||||
'vi-ep:key',
|
||||
'vi-ep:knife-fork',
|
||||
'vi-ep:lightning',
|
||||
'vi-ep:link',
|
||||
'vi-ep:list',
|
||||
'vi-ep:loading',
|
||||
'vi-ep:location',
|
||||
'vi-ep:location-filled',
|
||||
'vi-ep:location-information',
|
||||
'vi-ep:lock',
|
||||
'vi-ep:lollipop',
|
||||
'vi-ep:magic-stick',
|
||||
'vi-ep:magnet',
|
||||
'vi-ep:male',
|
||||
'vi-ep:management',
|
||||
'vi-ep:map-location',
|
||||
'vi-ep:medal',
|
||||
'vi-ep:memo',
|
||||
'vi-ep:menu',
|
||||
'vi-ep:message',
|
||||
'vi-ep:message-box',
|
||||
'vi-ep:mic',
|
||||
'vi-ep:microphone',
|
||||
'vi-ep:milk-tea',
|
||||
'vi-ep:minus',
|
||||
'vi-ep:money',
|
||||
'vi-ep:monitor',
|
||||
'vi-ep:moon',
|
||||
'vi-ep:moon-night',
|
||||
'vi-ep:more',
|
||||
'vi-ep:more-filled',
|
||||
'vi-ep:mostly-cloudy',
|
||||
'vi-ep:mouse',
|
||||
'vi-ep:mug',
|
||||
'vi-ep:mute',
|
||||
'vi-ep:mute-notification',
|
||||
'vi-ep:no-smoking',
|
||||
'vi-ep:notebook',
|
||||
'vi-ep:notification',
|
||||
'vi-ep:odometer',
|
||||
'vi-ep:office-building',
|
||||
'vi-ep:open',
|
||||
'vi-ep:operation',
|
||||
'vi-ep:opportunity',
|
||||
'vi-ep:orange',
|
||||
'vi-ep:paperclip',
|
||||
'vi-ep:partly-cloudy',
|
||||
'vi-ep:pear',
|
||||
'vi-ep:phone',
|
||||
'vi-ep:phone-filled',
|
||||
'vi-ep:picture',
|
||||
'vi-ep:picture-filled',
|
||||
'vi-ep:picture-rounded',
|
||||
'vi-ep:pie-chart',
|
||||
'vi-ep:place',
|
||||
'vi-ep:platform',
|
||||
'vi-ep:plus',
|
||||
'vi-ep:pointer',
|
||||
'vi-ep:position',
|
||||
'vi-ep:postcard',
|
||||
'vi-ep:pouring',
|
||||
'vi-ep:present',
|
||||
'vi-ep:price-tag',
|
||||
'vi-ep:printer',
|
||||
'vi-ep:promotion',
|
||||
'vi-ep:quartz-watch',
|
||||
'vi-ep:question-filled',
|
||||
'vi-ep:rank',
|
||||
'vi-ep:reading',
|
||||
'vi-ep:reading-lamp',
|
||||
'vi-ep:refresh',
|
||||
'vi-ep:refresh-left',
|
||||
'vi-ep:refresh-right',
|
||||
'vi-ep:refrigerator',
|
||||
'vi-ep:remove',
|
||||
'vi-ep:remove-filled',
|
||||
'vi-ep:right',
|
||||
'vi-ep:scale-to-original',
|
||||
'vi-ep:school',
|
||||
'vi-ep:scissor',
|
||||
'vi-ep:search',
|
||||
'vi-ep:select',
|
||||
'vi-ep:sell',
|
||||
'vi-ep:semi-select',
|
||||
'vi-ep:service',
|
||||
'vi-ep:set-up',
|
||||
'vi-ep:setting',
|
||||
'vi-ep:share',
|
||||
'vi-ep:ship',
|
||||
'vi-ep:shop',
|
||||
'vi-ep:shopping-bag',
|
||||
'vi-ep:shopping-cart',
|
||||
'vi-ep:shopping-cart-full',
|
||||
'vi-ep:shopping-trolley',
|
||||
'vi-ep:smoking',
|
||||
'vi-ep:soccer',
|
||||
'vi-ep:sold-out',
|
||||
'vi-ep:sort',
|
||||
'vi-ep:sort-down',
|
||||
'vi-ep:sort-up',
|
||||
'vi-ep:stamp',
|
||||
'vi-ep:star',
|
||||
'vi-ep:star-filled',
|
||||
'vi-ep:stopwatch',
|
||||
'vi-ep:success-filled',
|
||||
'vi-ep:sugar',
|
||||
'vi-ep:suitcase',
|
||||
'vi-ep:suitcase-line',
|
||||
'vi-ep:sunny',
|
||||
'vi-ep:sunrise',
|
||||
'vi-ep:sunset',
|
||||
'vi-ep:switch',
|
||||
'vi-ep:switch-button',
|
||||
'vi-ep:switch-filled',
|
||||
'vi-ep:takeaway-box',
|
||||
'vi-ep:ticket',
|
||||
'vi-ep:tickets',
|
||||
'vi-ep:timer',
|
||||
'vi-ep:toilet-paper',
|
||||
'vi-ep:tools',
|
||||
'vi-ep:top',
|
||||
'vi-ep:top-left',
|
||||
'vi-ep:top-right',
|
||||
'vi-ep:trend-charts',
|
||||
'vi-ep:trophy',
|
||||
'vi-ep:trophy-base',
|
||||
'vi-ep:turn-off',
|
||||
'vi-ep:umbrella',
|
||||
'vi-ep:unlock',
|
||||
'vi-ep:upload',
|
||||
'vi-ep:upload-filled',
|
||||
'vi-ep:user',
|
||||
'vi-ep:user-filled',
|
||||
'vi-ep:van',
|
||||
'vi-ep:video-camera',
|
||||
'vi-ep:video-camera-filled',
|
||||
'vi-ep:video-pause',
|
||||
'vi-ep:video-play',
|
||||
'vi-ep:view',
|
||||
'vi-ep:wallet',
|
||||
'vi-ep:wallet-filled',
|
||||
'vi-ep:warn-triangle-filled',
|
||||
'vi-ep:warning',
|
||||
'vi-ep:warning-filled',
|
||||
'vi-ep:watch',
|
||||
'vi-ep:watermelon',
|
||||
'vi-ep:wind-power',
|
||||
'vi-ep:zoom-in',
|
||||
'vi-ep:zoom-out'
|
||||
]
|
||||
}
|
||||