, default: TabHeadPosition.top}, // 页签位置
20 | }
21 |
22 | export type TabData = { item: PlTabComponent, index: number, active: boolean }
23 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-place.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/utils/constant.tsx:
--------------------------------------------------------------------------------
1 | export enum CheckboxStatus {
2 | uncheck = 'uncheck',
3 | check = 'check',
4 | minus = 'minus',
5 | }
6 |
7 | export const STATUS = {
8 | white: {icon: 'el-icon-info', status: 'white'},
9 | black: {icon: 'el-icon-info', status: 'black'},
10 | lite: {icon: 'el-icon-info', status: 'white'},
11 | dark: {icon: 'el-icon-info', status: 'black'},
12 | primary: {icon: 'el-icon-info', status: 'primary'},
13 | success: {icon: 'el-icon-success', status: 'success'},
14 | warn: {icon: 'el-icon-warning', status: 'warn'},
15 | error: {icon: 'el-icon-error', status: 'error'},
16 | info: {icon: 'el-icon-info', status: 'info'},
17 | } as { [k: string]: { icon: string, status: string } }
18 |
19 | export const noop = () => void 0
20 |
--------------------------------------------------------------------------------
/story/story.utils.ts:
--------------------------------------------------------------------------------
1 | export const StoryStatus = [
2 | {label: '基础', status: 'primary' as 'primary'},
3 | {label: '成功', status: 'success' as 'success'},
4 | {label: '警告', status: 'warn' as 'warn'},
5 | {label: '失败', status: 'error' as 'error'},
6 | {label: '帮助', status: 'info' as 'info'},
7 | ]
8 |
9 | export const StoryShapes = [
10 | {label: '圆角', shape: 'fillet' as 'fillet'},
11 | {label: '圆形', shape: 'round' as 'round'},
12 | {label: '方角', shape: 'square' as 'square'},
13 | ]
14 | export const StorySizes = [
15 | {label: '大', size: 'large' as 'large'},
16 | {label: '中', size: 'normal' as 'normal'},
17 | {label: '小', size: 'mini' as 'mini'},
18 | ]
19 |
20 | export const Modes = [
21 | 'fill',
22 | 'stroke',
23 | 'text',
24 | ]
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-first-aid-kit.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-refresh.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/use/useFunctionWrapper.ts:
--------------------------------------------------------------------------------
1 | import {getCurrentInstance} from 'vue'
2 |
3 | export function useFunctionWrapper any>(
5 | key: string,
6 | func: T,
7 | ): (...args: P) => ReturnType {
8 | return (...args) => {
9 | const ctx = getCurrentInstance() as any
10 | if (!ctx._use) ctx._use = {}
11 |
12 | if (!!ctx._use[key]) {
13 | throw new Error(`use ${key} can only be executed once!`)
14 | }
15 |
16 | return (ctx._use[key] = func(ctx, ...args))
17 | }
18 | }
19 |
20 | /*const func = useFunctionWrapper('', (ctx, name: string, age: number) => {
21 | return {
22 | sayHello: () => {},
23 | }
24 | })
25 |
26 | const data = func('111', 111)
27 | data.sayHello()*/
28 |
29 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-thumb.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlColorPicker/color-picker.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 |
3 | .pl-color-button {
4 | border-radius: 2px;
5 | height: 24px;
6 | width: 24px;
7 | border: solid 1px $ibc;
8 | padding: 2px;
9 | box-sizing: border-box;
10 | display: inline-block;
11 | cursor: pointer;
12 |
13 | .pl-color-button-background {
14 | background-size: 7px 7px;
15 | border-radius: 2px;
16 | box-sizing: border-box;
17 | height: 100%;
18 | width: 100%;
19 |
20 | .pl-color-button-color {
21 | height: 100%;
22 | width: 100%;
23 | }
24 | }
25 | }
26 |
27 | .pl-color-picker {
28 | @include sizeMixin(input) {
29 | .pl-color-button {
30 | height: $value/2;
31 | width: $value/2;
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/format/process/copyPlcList.ts:
--------------------------------------------------------------------------------
1 | import {tPlcType} from "../../utils/plc.type";
2 | import {deepcopy} from "plain-utils/object/deepcopy";
3 | import {PlcPublicAttrs} from "../../utils/plc.utils";
4 |
5 | /**
6 | * 浅复制一份plc数据,复制plc最外层对象以及plc.props数据,props数据是需要动态计算修改的。
7 | * state不能复制,因为需要其响应式属性。
8 | * @author 韦胜健
9 | * @date 2020/12/18 10:06
10 | */
11 | export function copyPlcList(plcList: tPlcType[]) {
12 | return plcList.map(plc => {
13 | const refer = plc.refer()
14 | const newPlc: tPlcType = {
15 | ...refer,
16 | ...deepcopy(PlcPublicAttrs),
17 | }
18 | newPlc.props = {...newPlc.props}
19 | if (newPlc.group) {
20 | newPlc.children = copyPlcList(newPlc.children)
21 | }
22 | return newPlc
23 | })
24 | }
--------------------------------------------------------------------------------
/src/packages/PlRate/rate.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-rate {
3 | position: relative;
4 | display: inline-block;
5 | cursor: pointer;
6 | overflow: hidden;
7 | vertical-align: text-bottom;
8 |
9 | .pl-rate-active {
10 | white-space: nowrap;
11 | overflow: hidden;
12 | position: absolute;
13 | left: 0;
14 | top: 0;
15 | bottom: 0;
16 | }
17 |
18 | @include statusMixin(rate) {
19 | color: $value;
20 | }
21 |
22 | &.pl-rate-disabled {
23 | color: $disabledText !important;
24 | cursor: not-allowed;
25 | }
26 |
27 | &.pl-rate-size-min {
28 | font-size: 14px;
29 | }
30 |
31 | &.pl-rate-size-normal {
32 | font-size: 16px;
33 | }
34 |
35 | &.pl-rate-size-large {
36 | font-size: 18px;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/story/pages/normal/DemoIcon.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent} from "plain-ui-composition";
2 | import {DemoRow} from "../../components/DemoRow";
3 | import {PlIcon} from "../../../src/packages/PlIcon";
4 |
5 | export default designComponent({
6 | setup() {
7 | return () => (
8 |
9 |
10 | 普通的文本:NORMAL normal
11 |
12 |
13 |
14 |
15 |
16 | {['primary', 'success', 'warn', 'error', 'info'].map(status => )}
17 |
18 |
19 | )
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/src/packages/PlCollapse/collapse.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 |
3 | .pl-collapse {
4 | .pl-collapse-title {
5 | font-size: 16px;
6 | color: $colorPrimary;
7 | padding: 16px 0;
8 | border-bottom: solid 1px $ibl;
9 | display: flex;
10 | align-items: center;
11 |
12 | .pl-icon {
13 | margin-right: 4px;
14 | }
15 |
16 | & > * {
17 | cursor: pointer;
18 | }
19 | }
20 |
21 | &.pl-collapse-is-open {
22 | .pl-collapse-title {
23 | & > .pl-icon:first-child {
24 | transform: rotate(90deg);
25 | }
26 | }
27 | }
28 | }
29 |
30 | .pl-collapse-group {
31 | border-bottom: solid 1px $ibl;
32 |
33 | .pl-collapse-title {
34 | border-bottom: none;
35 | border-top: solid 1px $ibl;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/story/components/DemoRow.utils.ts:
--------------------------------------------------------------------------------
1 | const DEMO_ROW_STORAGE_KEY = 'DEMO_ROW'
2 |
3 | export const DemoRowCache = (() => {
4 | let str = localStorage.getItem(DEMO_ROW_STORAGE_KEY)
5 | const cache: Record = !!str ? JSON.parse(str) : {}
6 |
7 | return {
8 | get: (id: string) => {
9 | const flag = cache[id]
10 | return flag == null ? true : flag
11 | },
12 | set: (id: string, flag: boolean) => {
13 | cache[id] = flag
14 | localStorage.setItem(DEMO_ROW_STORAGE_KEY, JSON.stringify(cache))
15 | },
16 | setAll: (flag: boolean) => {
17 | for (const key in cache) {
18 | cache[key] = flag
19 | }
20 | localStorage.setItem(DEMO_ROW_STORAGE_KEY, JSON.stringify(cache))
21 | },
22 | }
23 | })()
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "skipLibCheck": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "sourceMap": true,
13 | "baseUrl": ".",
14 | "types": [
15 | "webpack-env"
16 | ],
17 | "lib": [
18 | "esnext",
19 | "dom",
20 | "dom.iterable",
21 | "scripthost"
22 | ]
23 | },
24 | "include": [
25 | "src/**/*.ts",
26 | "src/**/*.tsx",
27 | "src/**/*.vue",
28 | "story/**/*.ts",
29 | "story/**/*.tsx",
30 | "story/**/*.vue",
31 | "tests/**/*.ts",
32 | "tests/**/*.tsx"
33 | ],
34 | "exclude": [
35 | "node_modules"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/src/packages/PlTablePro/table-pro.scss:
--------------------------------------------------------------------------------
1 | .pl-table-pro-pagination {
2 | padding: 16px 24px;
3 |
4 | .pl-pagination {
5 | justify-content: flex-end;
6 | }
7 | }
8 |
9 | .pl-table-pro-head {
10 | font-size: 14px;
11 | height: 56px;
12 | display: flex;
13 | align-items: center;
14 | justify-content: space-between;
15 | padding: 0 24px;
16 |
17 | .pl-table-pro-title {
18 | .pl-icon {
19 | margin-right: 4px;
20 | }
21 | }
22 |
23 | .pl-table-pro-operation {
24 | min-width: 350px;
25 | text-align: right;
26 |
27 | & > .pl-button {
28 | margin-left: 4px;
29 | }
30 | }
31 | }
32 |
33 | .pl-table-pro-fill {
34 | height: 100%;
35 | flex: 1;
36 | display: flex;
37 | flex-direction: column;
38 |
39 | .pl-table-pro-base-table {
40 | flex: 1;
41 | overflow: hidden;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/packages/PlTagInput/tag-input.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-tag-input {
3 | display: inline-block;
4 |
5 | .pl-tag {
6 | //margin-bottom: 8px;
7 |
8 | &:not(:last-child) {
9 | margin-right: 8px;
10 | }
11 | }
12 |
13 | .pl-input {
14 | //margin-bottom: 8px;
15 |
16 | .pl-input-inner {
17 | border-style: dashed;
18 | width: 100% !important;
19 | }
20 | }
21 |
22 | .pl-tag-input-not-edit {
23 | text-align: center;
24 | cursor: pointer;
25 | }
26 |
27 | @include statusMixin(tag-input) {
28 | .pl-tag-input-not-edit {
29 | color: $value;
30 | }
31 | }
32 |
33 | &.pl-tag-input-disabled {
34 | .pl-tag-input-not-edit {
35 | cursor: not-allowed;
36 | color: $disabledText;
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/story/pages/normal/DemoCheckbox.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .demo-checkbox {
3 | .demo-checkbox-row {
4 | & > * {
5 | margin-right: 1em;
6 | }
7 | }
8 |
9 | .demo-checkbox-custom-item {
10 | display: inline-flex;
11 | height: 80px;
12 | width: 80px;
13 | border-radius: 4px;
14 | border: solid 1px #f1f1f1;
15 | align-items: center;
16 | justify-content: center;
17 | background-color: #f1f1f1;
18 | cursor: pointer;
19 | user-select: none;
20 | transition: all 300ms $transition;
21 |
22 | &.demo-checkbox-custom-item-active {
23 | background-color: $colorPrimary;
24 | color: white;
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/story/app/navigator.utils.ts:
--------------------------------------------------------------------------------
1 | import {reactive} from "vue"
2 |
3 | export type AppRoute = {
4 | path: string,
5 | hash: string,
6 | }
7 |
8 | const getRoute = (): AppRoute => {
9 | let uri = decodeURIComponent(window.location.hash || '')
10 | if (uri.charAt(0) === '#' && uri.length > 0) {uri = uri.substring(1)}
11 | let [path, hash] = uri.split('#')
12 | if (!!path && path.charAt(0) === '/') {
13 | path = path.slice(1)
14 | }
15 | return {
16 | path,
17 | hash,
18 | }
19 | }
20 |
21 | export const Router = (() => {
22 | const state = reactive({
23 | route: getRoute(),
24 | go: (path: string) => {window.location.hash = encodeURIComponent(path)},
25 | })
26 | window.addEventListener('hashchange', () => {
27 | state.route = getRoute()
28 | })
29 | return state
30 | })();
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/edit/plc-input.tsx:
--------------------------------------------------------------------------------
1 |
2 | import {PlInput} from "../../../PlInput";
3 | import {designComponent} from "plain-ui-composition";
4 | import {PlcEmitsOptions, PlcPropsOptions} from "../utils/plc.utils";
5 | import {PlcScopeSlotsOptions} from "../utils/plc.scope-slots";
6 | import {useExternalPlc} from "../core/useExternalPlc";
7 |
8 | export default designComponent({
9 | name: 'plc-input',
10 | props: {
11 | ...PlcPropsOptions,
12 | },
13 | scopeSlots: PlcScopeSlotsOptions,
14 | emits: PlcEmitsOptions,
15 | setup({props, slots, scopeSlots, event}) {
16 | return useExternalPlc({
17 | props, scopeSlots, slots, event, defaultScopeSlots: {
18 | edit: ({row, plc}) => !plc.props.field ? null :
19 | }
20 | })
21 | },
22 | })
23 |
--------------------------------------------------------------------------------
/src/packages/PlDate/useDate.tsx:
--------------------------------------------------------------------------------
1 | import {PlDatePanel} from "./panel/PlDatePanel";
2 | import {createUseEditPopperAgent} from "../useEditPopperAgent/createAgentGetter";
3 |
4 | export const useDate = createUseEditPopperAgent({
5 | name: 'date',
6 | render: (attrs) => ,
7 | defaultPopperAttrs: {
8 | transition: 'pl-transition-popper-drop',
9 | },
10 | defaultRenderAttrs: {
11 | async onChange() {
12 | let renderAttrs: any = this.state.option.serviceOption.renderAttrs
13 | if (typeof renderAttrs === "function") renderAttrs = renderAttrs()
14 | if (!(
15 | renderAttrs.datetime ||
16 | renderAttrs.multiple ||
17 | renderAttrs.range
18 | )) {
19 | this.hide()
20 | }
21 | },
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/src/packages/PlSelect/useSelect.tsx:
--------------------------------------------------------------------------------
1 | import {createUseEditPopperAgent} from "../useEditPopperAgent/createAgentGetter";
2 | import {PlSelectPanel} from "./PlSelectPanel";
3 |
4 | export const useSelect = createUseEditPopperAgent({
5 | name: 'select',
6 | render: (attrs) => {
7 | return
8 | },
9 | defaultPopperAttrs: {
10 | transition: 'pl-transition-popper-drop',
11 | sizeEqual: true,
12 | arrow: false,
13 | },
14 | defaultRenderAttrs: {
15 | async onChange() {
16 | let renderAttrs: any = this.state.option.serviceOption.renderAttrs
17 | if (typeof renderAttrs === "function") renderAttrs = renderAttrs()
18 | /*非多选的情况下,默认点击关闭下拉框*/
19 | if (!renderAttrs.multiple) {
20 | this.hide()
21 | }
22 | }
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/src/packages/PlTime/panel/time-base-panel.scss:
--------------------------------------------------------------------------------
1 | @import "./time.public";
2 |
3 | @include theme {
4 | .pl-time-base-panel {
5 | display: inline-block;
6 | position: relative;
7 | color: $itc;
8 |
9 | &:before {
10 | position: absolute;
11 | top: $time-size*3;
12 | left: 0;
13 | right: 0;
14 | height: 1px;
15 | background-color: $ibl;
16 | content: '';
17 | pointer-events: none;
18 | }
19 |
20 | &:after {
21 | position: absolute;
22 | top: $time-size*4;
23 | left: 0;
24 | right: 0;
25 | height: 1px;
26 | background-color: $ibl;
27 | content: '';
28 | pointer-events: none;
29 | }
30 |
31 | .pl-time-base-column {
32 | border: none;
33 |
34 | & + .pl-time-base-column {
35 | border-left: dashed 1px $ibc;
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/packages/PlTime/time.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-time {
3 | .pl-time-inner {
4 | display: flex;
5 | align-items: stretch;
6 | height: 100%;
7 |
8 | & > span {
9 | color: $icc;
10 | }
11 |
12 | .pl-time-input-inner {
13 | border: none;
14 | outline: none;
15 | width: 90px;
16 | height: 100%;
17 | background: transparent;
18 | }
19 | }
20 |
21 | &.pl-time-range {
22 | .pl-time-input-inner {
23 | text-align: center;
24 | }
25 | }
26 |
27 | .pl-input-inner {
28 | width: auto !important;
29 | }
30 | }
31 |
32 | .pl-time-panel-foot {
33 | padding: 8px 8px 0 8px;
34 | border-top: solid 1px $ibl;
35 | display: flex;
36 | justify-content: center;
37 |
38 | & > * + * {
39 | margin-left: 8px;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-view.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlColorPicker/sub/color-alpha-slider.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-color-alpha-slider {
3 | width: 10px;
4 | background-size: 10px 10px;
5 | position: relative;
6 | cursor: pointer;
7 | border-radius: 10px;
8 | display: inline-block;
9 |
10 | .pl-color-alpha-shadow, .pl-color-alpha-thumb {
11 | pointer-events: none;
12 | }
13 |
14 | .pl-color-alpha-shadow {
15 | position: absolute;
16 | top: 0;
17 | left: 0;
18 | right: 0;
19 | bottom: 0;
20 | border-radius: 10px;
21 | }
22 |
23 | .pl-color-alpha-thumb {
24 | width: 10px;
25 | height: 10px;
26 | border-radius: 10px;
27 | border: solid 1px $colorInfo;
28 | display: inline-block;
29 | background-color: white;
30 | position: absolute;
31 | top: 0;
32 | left: 0;
33 | box-sizing: border-box;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-rank.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/story/pages/table-pro/table-files.tsx:
--------------------------------------------------------------------------------
1 | import {designPage} from "plain-ui-composition";
2 |
3 | import useTableOption from "../../init/useTableOption";
4 | import {Plc, PlcDate, PlTablePro} from "../../../src";
5 |
6 | export const demo1 = designPage(() => {
7 |
8 | const option = useTableOption({
9 | url: '/upload',
10 | fill: true,
11 | enable: {
12 | insert: false,
13 | update: false,
14 | },
15 | })
16 |
17 | return () => <>
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 |
27 | })
28 |
--------------------------------------------------------------------------------
/src/packages/PlTooltip/tooltip.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-tooltip {
3 | .plain-popper-content .pl-popper-content-inner {
4 | color: inherit;
5 | padding: 8px 12px !important;
6 | }
7 |
8 | &.pl-tooltip-theme-dark {
9 | .plain-popper-content {
10 | background-color: black !important;
11 |
12 | &, & .pl-popper-content-inner, & .pl-popper-title {
13 | color: white;
14 | }
15 | }
16 | }
17 |
18 | &.pl-tooltip-theme-light {
19 | .plain-popper-content {
20 | background-color: white !important;
21 |
22 | &, & .pl-popper-content-inner {
23 | color: $itc !important;
24 | }
25 | }
26 | }
27 | }
28 |
29 | .pl-tooltip-reference {
30 | display: inline-block;
31 | overflow: hidden;
32 | text-overflow: ellipsis;
33 | white-space: nowrap;
34 | vertical-align: baseline;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/packages/PlDropdownMenu/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent} from "plain-ui-composition";
2 | import PlDropdown from "../PlDropdown";
3 |
4 | export const PlDropdownMenu = designComponent({
5 | name: 'pl-dropdown-menu',
6 | props: {},
7 | emits: {
8 | onClickOption: (e: MouseEvent, val: any) => true,
9 | },
10 | slots: ['default'],
11 | provideRefer: true,
12 | setup({props, slots, event: {emit}}) {
13 |
14 | const dropdown = PlDropdown.use.inject()
15 |
16 | const handler = {
17 | clickOption: (e: MouseEvent, val: any) => {
18 | emit.onClickOption(e, val)
19 | dropdown.handler.clickDropdownOption(e)
20 | }
21 | }
22 |
23 | return {
24 | refer: {
25 | handler,
26 | },
27 | render: slots.default
28 | }
29 | },
30 | })
31 |
32 | export default PlDropdownMenu
33 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-alarm-clock.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlPopper/refershPopperReference.tsx:
--------------------------------------------------------------------------------
1 | import {inject, provide} from "plain-ui-composition"
2 | import {SimpleFunction} from "plain-ui-composition"
3 |
4 | const REFRESH_POPPER_REFERENCE = '@@REFRESH_POPPER_REFERENCE'
5 |
6 | /**
7 | * 因为 Popper 是无根节点,所以比较难检测到reference节点变化的情况。为了优化这一块的内容
8 | * 有两种方式,一种是在使用Popper组件的时候,reference包裹一层不变的节点,缺点是会破坏无根
9 | * 节点的结构。另一种是,提供一个 refreshPopperReference 函数,子组件可以注入这个函数,当子组件
10 | * 知道自己的reference节点变化之后调用这个函数通知Popper重新reference。
11 | *
12 | * @author 韦胜健
13 | * @date 2020/11/19 11:00
14 | */
15 | export const refreshPopperReference = {
16 | provide: (refresh: SimpleFunction) => {
17 | provide(REFRESH_POPPER_REFERENCE, refresh)
18 | },
19 | inject: (defaultValue = null) => {
20 | const refresh = inject(REFRESH_POPPER_REFERENCE, defaultValue) as null | SimpleFunction
21 | return {
22 | freshPopperReference: () => !!refresh && refresh()
23 | }
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/src/packages/PlColorPicker/color-panel.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-color-panel {
3 | display: inline-block;
4 | font-size: 0;
5 | padding: 8px 12px;
6 |
7 | & > div:first-child {
8 | white-space: nowrap;
9 | }
10 |
11 | .pl-color-hue-slider {
12 | margin-top: 8px;
13 | display: block;
14 | }
15 |
16 | .pl-color-alpha-slider {
17 | margin-left: 8px;
18 | }
19 |
20 | .pl-color-panel-input-group {
21 | margin-top: 8px;
22 |
23 | .pl-input {
24 |
25 | .pl-input-inner {
26 | text-align: center;
27 | border-top-right-radius: 0;
28 | border-bottom-right-radius: 0;
29 | border-right: none;
30 | }
31 | }
32 |
33 | .pl-button-group {
34 | & > .pl-button:first-child {
35 | border-top-left-radius: 0;
36 | border-bottom-left-radius: 0;
37 | border-left: none;
38 | }
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/packages/initialize/index.tsx:
--------------------------------------------------------------------------------
1 | import {tUseTableOption} from "../createUseTableOption";
2 | import {iUseHttp} from "../useHttp/useHttp.utils";
3 | import {iUseAddressConfig} from "../useAddress/useAddress.utils";
4 | import {iUseOvConfig} from "../useOv/useOv.utils";
5 |
6 | export interface InitializeConfigState {
7 | useTableOption: tUseTableOption,
8 | useObjectOption: tUseTableOption,
9 | useHttp: iUseHttp,
10 | useAddressConfig: iUseAddressConfig,
11 | useOvConfig: iUseOvConfig,
12 | getExceljs: () => Promise,
13 | getFileSaver: () => Promise,
14 | }
15 |
16 | let state: InitializeConfigState | null = null
17 |
18 | export function initialize(getter: () => InitializeConfigState) {state = getter()}
19 |
20 | export function getInitialConfigState(k: K) {
21 | if (!state) {throw new Error('请先调用 initialize 初始化 InitializeConfigState')}
22 | return state[k]
23 | }
24 |
25 | export default initialize
26 |
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/edit/plc-number.tsx:
--------------------------------------------------------------------------------
1 |
2 | import {PlNumber} from "../../../PlNumber";
3 | import {designComponent} from "plain-ui-composition";
4 | import {PlcEmitsOptions, PlcPropsOptions} from "../utils/plc.utils";
5 | import {PlcScopeSlotsOptions} from "../utils/plc.scope-slots";
6 | import {useExternalPlc} from "../core/useExternalPlc";
7 |
8 | export default designComponent({
9 | name: 'plc-number',
10 | props: {
11 | ...PlcPropsOptions,
12 | filterName: {type: String, default: 'number'},
13 | filterHandler: {type: String, default: '范围'},
14 | },
15 | scopeSlots: PlcScopeSlotsOptions,
16 | emits: PlcEmitsOptions,
17 | setup({props, slots, scopeSlots, event}) {
18 | return useExternalPlc({
19 | props, scopeSlots, slots, event, defaultScopeSlots: {
20 | edit: ({row, plc}) => !plc.props.field ? null :
21 | }
22 | })
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-debug.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlCascadePanel/useCascade.tsx:
--------------------------------------------------------------------------------
1 | import './cascade-service.scss'
2 | import {createUseEditPopperAgent} from "../useEditPopperAgent/createAgentGetter";
3 | import PlCascadePanel from "./index";
4 | import {delay} from "plain-utils/utils/delay";
5 |
6 | export const useCascade = createUseEditPopperAgent({
7 | name: 'cascade',
8 | render(attrs) {
9 | return
10 | },
11 | defaultPopperAttrs: {
12 | transition: 'pl-transition-popper-drop',
13 | },
14 | defaultRenderAttrs: {
15 | async onChange() {
16 | let renderAttrs: any = this.state.option.serviceOption.renderAttrs
17 | if (typeof renderAttrs === "function") renderAttrs = renderAttrs()
18 | if (!renderAttrs.selectBranch) {
19 | // 因为change之后,会引发 cascade panels 重新渲染没有宽度以及高度,导致过度动画失效,这里再change之后延迟再关闭
20 | await delay(120)
21 | this.hide()
22 | }
23 | },
24 | },
25 | })
26 |
--------------------------------------------------------------------------------
/src/packages/PlTabs/tabs.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-tabs {
3 | .pl-tabs-collector {
4 | height: 0;
5 | width: 0;
6 | overflow: hidden;
7 | display: block;
8 | }
9 |
10 | .pl-tabs-header-item {
11 | cursor: pointer;
12 | user-select: none;
13 |
14 | .pl-icon {
15 | margin-left: 8px;
16 | }
17 |
18 | &:not(.pl-tabs-header-item-active) {
19 | .pl-icon {
20 | color: $icc;
21 | }
22 | }
23 | }
24 |
25 | .pl-tabs-header-item-operator {
26 | flex-grow: 1;
27 | flex-shrink: 0;
28 | position: sticky;
29 | right: 4px;
30 | height: 45px;
31 | line-height: 45px;
32 | padding-left: 16px;
33 | justify-self: flex-end;
34 | display: inline-flex;
35 | align-items: center;
36 | justify-content: flex-end;
37 | background-color: white;
38 |
39 | & > * + * {
40 | margin-left: 4px;
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-chat-dot-round.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/story/app/AppNavigator.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, reactive, watch} from 'plain-ui-composition'
2 | import {Fragment} from 'vue'
3 | import {Router} from "./navigator.utils";
4 |
5 | export const AppNavigator = designComponent({
6 | setup() {
7 |
8 | const state = reactive({
9 | Page: null as any
10 | })
11 |
12 | watch(() => Router.route.path, async (path) => {
13 | if (!path) path = 'normal/DemoButton'
14 | if (path.charAt(0) === '/') {path = path.slice(1)}
15 |
16 | const Components = Object.values(await import('../pages/' + path)) as any[]
17 | state.Page = Components.map((Component, index) => (
18 |
19 |
20 |
21 | ))
22 | }, {immediate: true})
23 |
24 | return () => (
25 |
26 | {state.Page}
27 |
28 | )
29 | },
30 | })
31 |
--------------------------------------------------------------------------------
/src/packages/PlRadioInner/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, useClasses, useRefs} from "plain-ui-composition"
2 | import './radio-inner.scss'
3 |
4 | export const PlRadioInner = designComponent({
5 | name: 'pl-radio-inner',
6 | props: {
7 | checkStatus: {type: String}, // check,uncheck
8 | },
9 | inheritPropsType: SVGElement,
10 | setup({props}) {
11 |
12 | const {refs, onRef} = useRefs({el: SVGElement})
13 |
14 | const classes = useClasses(() => [
15 | 'pl-radio-inner',
16 | `pl-radio-inner-${props.checkStatus}`,
17 | ])
18 |
19 | return {
20 | refer: {refs},
21 | render: () => (
22 |
25 | )
26 | }
27 | },
28 | })
29 |
30 | export default PlRadioInner
31 |
--------------------------------------------------------------------------------
/src/packages/createUseTableOption/use/filter/distinct.filter.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-table-pro-distinct-filter-item {
3 | display: flex;
4 | align-items: stretch;
5 | line-height: 28px;
6 |
7 | padding: 8px 0;
8 |
9 | background-color: white;
10 | transition: background-color 300ms linear;
11 |
12 | &:not(:last-child) {
13 | border-bottom: solid 1px $ibl;
14 | }
15 |
16 | &:hover {
17 | background-color: #f6f6f6;
18 | }
19 |
20 | .pl-table-pro-distinct-filter-item-title {
21 | color: $colorPrimary;
22 | margin: 0 8px;
23 | }
24 |
25 | .pl-table-pro-distinct-filter-item-tags {
26 |
27 | cursor: pointer;
28 |
29 | & > div {
30 | display: inline-block;
31 | font-size: 12px;
32 | background-color: $colorPrimaryLight;
33 | padding: 0 12px;
34 | margin: 1px 2px;
35 | border-radius: 4px;
36 | }
37 | }
38 |
39 | .pl-table-pro-distinct-filter-item-button {
40 |
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/edit/plc-rate.tsx:
--------------------------------------------------------------------------------
1 |
2 | import {PlRate} from "../../../PlRate";
3 | import {designComponent} from "plain-ui-composition";
4 | import {createPlcPropOptions, PlcEmitsOptions} from "../utils/plc.utils";
5 | import {PlcScopeSlotsOptions} from "../utils/plc.scope-slots";
6 | import {useExternalPlc} from "../core/useExternalPlc";
7 |
8 | export default designComponent({
9 | name: 'plc-rate',
10 | props: {
11 | ...createPlcPropOptions({
12 | addEditPadding: true,
13 | }),
14 | },
15 | scopeSlots: PlcScopeSlotsOptions,
16 | emits: PlcEmitsOptions,
17 | setup({props, slots, scopeSlots, event}) {
18 | return useExternalPlc({
19 | props, scopeSlots, slots, event, defaultScopeSlots: {
20 | normal: ({row, plc}) => !plc.props.field ? null : ,
21 | edit: ({row, plc}) => !plc.props.field ? null : ,
22 | }
23 | })
24 | },
25 | })
26 |
--------------------------------------------------------------------------------
/src/packages/PlEmpty/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, useStyles} from "plain-ui-composition";
2 |
3 | // @ts-ignore
4 | import NO_DATA_IMG from './assets/no_data.svg'
5 | import './empty.scss'
6 |
7 | export const PlEmpty = designComponent({
8 | name: 'pl-empty',
9 | props: {
10 | height: {type: String, default: '200px'},
11 | fontSize: {type: String, default: '14px'},
12 | label: {type: String, default: '暂无数据...'},
13 | },
14 | slots: ['labelContent'],
15 | setup({props}) {
16 |
17 | const styles = useStyles(styles => {
18 | styles.fontSize = props.fontSize
19 | })
20 | const imgStyles = useStyles((styles) => {
21 | styles.height = props.height
22 | })
23 |
24 | return () => (
25 |
26 |

27 |
{props.label}
28 |
29 | )
30 | },
31 | })
32 |
33 | export default PlEmpty
34 |
--------------------------------------------------------------------------------
/src/packages/createUseTableOption/use/setting/use.setting.filter.all.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-table-pro-setting-all-filter {
3 |
4 | background-color: #fafafa;
5 | min-height: 100%;
6 | width: 100%;
7 | display: inline-block;
8 | box-sizing: border-box;
9 | padding: 16px;
10 |
11 | & > * {
12 | margin-bottom: 16px;
13 |
14 | &:first-child {
15 | text-align: right;
16 | }
17 | }
18 |
19 | .pl-table-pro-setting-all-filter-item {
20 | background-color: white;
21 | box-sizing: border-box;
22 | border-radius: 2px;
23 | box-shadow: 1px 1px 8px 1px #e2e3e4;
24 |
25 | .pl-table-pro-setting-all-filter-item-head {
26 | font-size: 16px;
27 | font-weight: 600;
28 | border-bottom: solid 1px $ibl;
29 | padding: 16px;
30 | display: flex;
31 | justify-content: space-between;
32 | }
33 |
34 | .pl-table-pro-setting-all-filter-item-body {
35 | padding: 16px;
36 | }
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/utils/ClickBodyListener.tsx:
--------------------------------------------------------------------------------
1 | interface ClickListener {
2 | (e: MouseEvent): void
3 | }
4 |
5 | export const ClickBodyListener = (() => {
6 |
7 | const state = {
8 | handlers: [] as ClickListener[],
9 | disabled: false,
10 | }
11 |
12 | const onClickBody = (e: MouseEvent) => {
13 | if (state.disabled) {return}
14 | state.handlers.forEach(h => h(e))
15 | }
16 |
17 | document.body.addEventListener('click', onClickBody, true)
18 |
19 | const disable = () => state.disabled = true
20 | const enable = () => state.disabled = false
21 | const listen = (handler: ClickListener) => {
22 | state.handlers.indexOf(handler) === -1 && state.handlers.push(handler)
23 | return () => eject(handler)
24 | }
25 | const eject = (handler: ClickListener) => {
26 | const index = state.handlers.indexOf(handler)
27 | index >= 1 && state.handlers.splice(index, 1)
28 | }
29 |
30 | return {
31 | disable,
32 | enable,
33 | listen,
34 | eject,
35 | }
36 | })();
--------------------------------------------------------------------------------
/src/packages/PlTable/table/use/useBindScroll.ts:
--------------------------------------------------------------------------------
1 | import {TableHoverPart} from "../utils/table.utils";
2 | import {ref} from "plain-ui-composition";
3 |
4 | /**
5 | * 绑定表头表体联动滚动
6 | * @author 韦胜健
7 | * @date 2020/12/18 16:27
8 | */
9 | export function useBindScroll(
10 | event: {
11 | emit: {
12 | onScrollLeft: (scrollLeft: number, part: TableHoverPart) => void,
13 | },
14 | on: {
15 | onScrollLeft: (cb: (scrollLeft: number, part: TableHoverPart) => void) => void,
16 | },
17 | }
18 | ) {
19 | const hoverPart = ref(null as null | TableHoverPart)
20 | return {
21 | bindScroll: (part: TableHoverPart, updateLeft: (left: number, part: TableHoverPart) => void) => {
22 | event.on.onScrollLeft((left, part) => updateLeft(left, part))
23 | return {
24 | onMouseenter: () => hoverPart.value = part,
25 | onScroll: (e: Event) => hoverPart.value === part && event.emit.onScrollLeft((e as any).target.scrollLeft, part)
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/packages/PlLoadingMask/loading-mask.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-loading-mask {
3 | color: $colorPrimary;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | flex-direction: column;
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 | right: 0;
12 | bottom: 0;
13 | background-color: rgba(255, 255, 255, 0.5);
14 | transition: all linear 300ms;
15 | transform-origin: center center;
16 | cursor: progress;
17 | pointer-events: auto;
18 |
19 | .pl-loading {
20 | margin-bottom: 20px;
21 | font-size: 32px;
22 |
23 | & + span {
24 | font-size: 14px;
25 | }
26 | }
27 |
28 | &.pl-loading-mask-unlock {
29 | pointer-events: none;
30 | }
31 |
32 | &.pl-loading-mask-fixed-position {
33 | position: fixed;
34 | }
35 | }
36 | }
37 |
38 | .pl-transition-loading-mask-enter-active, .pl-transition-loading-mask-leave-active {
39 | opacity: 1;
40 | }
41 |
42 | .pl-transition-loading-mask-enter-from, .pl-transition-loading-mask-leave-to {
43 | opacity: 0;
44 | }
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/standard/draggier/core/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取拖拽的rowEl对象
3 | * @author 韦胜健
4 | * @date 2020/8/19 23:55
5 | */
6 | import {hasClass} from "plain-utils/dom/hasClass";
7 |
8 | export function getRowEl(e: MouseEvent, rowClass: string): HTMLElement {
9 | let rowEl = e.target as HTMLElement
10 | while (!!rowEl && !hasClass(rowEl, rowClass)) {
11 | rowEl = rowEl.parentNode as HTMLElement
12 | }
13 | if (!rowEl) {
14 | throw new Error(`can't find item element!`)
15 | }
16 | return rowEl
17 | }
18 |
19 | /**
20 | * 获取可以滚动的父组件
21 | * @author 韦胜健
22 | * @date 2020/8/19 23:50
23 | */
24 | export function getScrollParent(el: HTMLElement): HTMLElement {
25 | while (!!el && el.scrollHeight <= el.offsetHeight) {
26 | el = el.parentNode as HTMLElement
27 | }
28 | return el
29 | }
30 |
31 | /**
32 | * 获取行el对象的所有兄弟节点
33 | * @author 韦胜健
34 | * @date 2020/8/19 23:57
35 | */
36 | export function getRowElList(el: HTMLElement, rowClass: string): HTMLElement[] {
37 | return Array.from(el.parentNode!.querySelectorAll(`.${rowClass}`))
38 | }
--------------------------------------------------------------------------------
/src/utils/createFlagManager.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 给mark对象使用,用来创建管理flag的对象
3 | * @author 韦胜健
4 | * @date 2020/11/27 11:38
5 | */
6 | import {UnwrapRef} from "plain-ui-composition";
7 | import {reactive} from "plain-ui-composition";
8 |
9 | export function createFlagManager() {
10 | return {
11 | state: reactive({
12 | map: {} as { [k: string]: T }
13 | }),
14 | get(key: string) {
15 | return this.state.map[key]
16 | },
17 | set(key: string, val: UnwrapRef) {
18 | /*if (this.state.map.hasOwnProperty(key)) {
19 | this.state.map[key] = val
20 | } else {
21 | set(this.state.map, key, val)
22 | }*/
23 | this.state.map[key] = val
24 | },
25 | getActiveKeys(): string[] {
26 | const keys: string[] = []
27 | for (let key in this.state.map) {
28 | if (!!this.state.map[key]) {
29 | keys.push(key)
30 | }
31 | }
32 | return keys
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-turn-off-microphone.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/useDialog/dialog-service.scss:
--------------------------------------------------------------------------------
1 | .pl-dialog-service-edit {
2 | .pl-dialog-content {
3 | & > .pl-textarea {
4 | display: block !important;
5 | height: 100%;
6 |
7 | .pl-textarea-inner {
8 | height: 100%;
9 | }
10 | }
11 | }
12 | }
13 |
14 | @include theme {
15 | .pl-dialog-service {
16 | @include statusMixin(dialog-service) {
17 | .pl-dialog-head {
18 | background-color: rgba($value, 0.05);
19 | color: $value;
20 | /*position: relative;
21 |
22 | &:after {
23 | position: absolute;
24 | left: 0;
25 | right: 0;
26 | top: 0;
27 | height: 4px;
28 | background-color: $value;
29 | content: '';
30 | }*/
31 | border-top: solid 4px rgba($value, 0.85);
32 |
33 | .pl-dialog-service-head {
34 | color: $value;
35 |
36 | .pl-dialog-service-status-icon {
37 | font-size: 18px;
38 | margin-right: 4px;
39 | vertical-align: -0.25em;
40 | }
41 | }
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-undo.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/story/pages/test/test.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
测试全局注册的组件
4 |
5 |
6 | (with context) message
7 |
8 |
9 | (without context) message
10 |
11 |
12 |
13 |
14 | (with context) notice
15 |
16 |
17 | (without context) notice
18 |
19 |
20 |
21 |
22 |
23 |
44 |
45 |
48 |
--------------------------------------------------------------------------------
/src/packages/PlTree/utils/tree.utils.ts:
--------------------------------------------------------------------------------
1 | import {TreeNode} from "./type";
2 |
3 | const basePadding = 8
4 |
5 | export const TreeUtils = {
6 | getPaddingLeft: (level: number, intent: number) => {
7 | return basePadding + (level - 1) * intent
8 | },
9 | /**
10 | * 计算treeNode的样式
11 | * @author 韦胜健
12 | * @date 2020/11/28 9:25
13 | */
14 | getTreeNodeStyles: (level: number, intent: number, nodeHeight: number) => {
15 | const basePadding = 8
16 | return {
17 | paddingLeft: `${TreeUtils.getPaddingLeft(level, intent)}px`,
18 | paddingRight: `${basePadding}px`,
19 | height: `${nodeHeight}px`,
20 | }
21 | },
22 | /**
23 | * 计算tree node的class
24 | * @author 韦胜健
25 | * @date 2020/11/28 10:35
26 | */
27 | getTreeNodeClasses: (node: TreeNode, current?: string | number) => {
28 | return [
29 | 'pl-tree-node',
30 | {
31 | 'pl-tree-node-current': node.key == current,
32 | 'pl-tree-node-not-checkable': !node.isCheckable,
33 | }
34 | ]
35 | },
36 | }
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-redo.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/standard/draggier/core/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 拖拽排序实现函数的类型
3 | * @author 韦胜健
4 | * @date 2020/8/21 10:07
5 | */
6 | import {useListDraggierNotVirtual} from "./use-list-draggier";
7 | import {useListDraggierWithVirtual} from "./use-list-draggier.virtual";
8 | import {PlainScroll} from "../../../../../PlScroll";
9 |
10 |
11 | export interface UseListDraggierType {
12 | (option: {
13 | rowClass: string, // 行的class,要确保只有行所在的dom对象有这个class,其子节点是没有这个class的
14 | onChange: (start: number, end: number) => void | Promise, // 拖拽导致排序变化动作
15 | virtual?: boolean, // 是否为虚拟滚动
16 | getScroll: () => PlainScroll,
17 | }): {
18 | handler: {
19 | mousedown: (e: MouseEvent) => void,
20 | }
21 | }
22 | }
23 |
24 | export const useListDraggier: UseListDraggierType = (option) => {
25 | if (option.virtual) {
26 | return useListDraggierWithVirtual(option)
27 | } else {
28 | return useListDraggierNotVirtual(option)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-date.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlColorButton/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, useRefs} from 'plain-ui-composition'
2 | import './color-button.scss'
3 | // @ts-ignore
4 | import opacityPng from './opacity.png'
5 |
6 | export const PlColorButton = designComponent({
7 | name: 'pl-color-button',
8 | props: {
9 | color: {type: String}
10 | },
11 | emits: {
12 | onClick: (e: MouseEvent) => true,
13 | },
14 | inheritPropsType: HTMLDivElement,
15 | setup({props, event: {emit}}) {
16 |
17 | const {refs, onRef} = useRefs({
18 | el: HTMLDivElement,
19 | })
20 |
21 | return {
22 | refer: {
23 | refs,
24 | },
25 | render: () => (
26 |
31 | )
32 | }
33 | },
34 | })
35 |
36 | export default PlColorButton
37 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-setting.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlProgressBar/progress-bar.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-progress-bar {
3 | display: inline-flex;
4 | align-items: center;
5 | vertical-align: middle;
6 |
7 | .pl-progress-bar-content {
8 | display: inline-block;
9 | width: 50px;
10 | text-align: left;
11 | font-size: 14px;
12 | padding: 0 6px;
13 |
14 | .pl-icon {
15 | vertical-align: -0.1em;
16 | }
17 | }
18 |
19 | .pl-progress-bar-outer {
20 | display: inline-flex;
21 | align-items: center;
22 | overflow: hidden;
23 |
24 | .pl-progress-bar-inner {
25 | height: 100%;
26 | display: inline-flex;
27 | align-items: center;
28 | justify-content: flex-end;
29 |
30 | transition: all $transition 300ms;
31 |
32 | .pl-progress-bar-content {
33 | text-align: right;
34 |
35 | span {
36 | color: white;
37 | }
38 | }
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/packages/PlTree/utils/type.ts:
--------------------------------------------------------------------------------
1 | import {TreeDropType, TreeNodeCheckStatus} from "./tree-constant";
2 | import {PlainObject} from "plain-utils/utils/event";
3 | import {VueNode} from "plain-ui-composition";
4 |
5 | export type TreeNode = {
6 | key: string,
7 | data: PlainObject,
8 | level: number,
9 | parentRef: () => TreeNode | null,
10 | selfRef: () => TreeNode,
11 |
12 | index: number,
13 | empty: boolean,
14 |
15 | readonly childrenData?: PlainObject[]
16 | readonly label?: string,
17 | children?: TreeNode[],
18 | readonly checkStatus: TreeNodeCheckStatus,
19 |
20 | expand: boolean,
21 | check: boolean,
22 | loading: boolean,
23 | loaded: boolean,
24 |
25 | readonly isCheckable: boolean,
26 | readonly isLeaf: boolean,
27 | readonly isVisible: boolean,
28 | }
29 |
30 | export namespace TreePropsType {
31 | export interface renderContent {(data: { node: TreeNode, index: number }): VueNode}
32 |
33 | export interface nodeIcon {(node: TreeNode): string}
34 |
35 | export interface allowDrag {(node: TreeNode): boolean}
36 |
37 | export interface allowDrop {(startNode: TreeNode, moveNode: TreeNode, dropType: TreeDropType): boolean}
38 | }
39 |
--------------------------------------------------------------------------------
/story/pages/table-pro/format-row.tsx:
--------------------------------------------------------------------------------
1 | import {designPage} from "plain-ui-composition";
2 |
3 | import useTableOption from "../../init/useTableOption";
4 | import {PlcInput, PlcTextarea, PlTablePro} from "../../../src";
5 | import {DemoRow} from "../../components/DemoRow";
6 |
7 | export const demo1 = designPage(() => {
8 |
9 | const provinceOption = useTableOption({
10 | url: '/address',
11 | filterParam: {queries: [{field: 'deep', value: '0', operator: '='}]},
12 | defaultNewRow: {deep: 0,},
13 | showRows: 5,
14 | hooks: {
15 | onFormatRow: row => {
16 | row.formatField = `_${row.code}`
17 | }
18 | },
19 | })
20 |
21 | return () => <>
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | >
32 |
33 | })
34 |
--------------------------------------------------------------------------------
/src/packages/PlList/list.tsx:
--------------------------------------------------------------------------------
1 | import './list.scss'
2 | import {designComponent} from "plain-ui-composition";
3 | import {TransitionGroup} from 'vue'
4 |
5 | export const PlList = designComponent({
6 | name: 'pl-list',
7 | props: {
8 | direction: { //item入场出场动画 'left', 'right', 'top', 'bottom', 'left-top', 'top-left', 'right-top', 'top-right', 'left-bottom', 'bottom-left', 'right-bottom', 'bottom-right'
9 | type: String,
10 | default: 'bottom-right',
11 | },
12 | tag: {type: String, default: 'div'},
13 | disabled: {type: Boolean},
14 | },
15 | slots: ['default'],
16 | setup({props, slots}) {
17 | return {
18 | render: () => {
19 | const Component = (props.disabled ? props.tag : TransitionGroup) as any
20 | return (
21 |
26 | {slots.default()}
27 |
28 | )
29 | }
30 | }
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/src/packages/useLoading/full/index.tsx:
--------------------------------------------------------------------------------
1 | import {createServiceWithoutContext, createUseService} from "../../PlRoot/registryRootService";
2 | import {createDefaultManager} from "../../PlRoot/createDefaultManager";
3 | import Service from './Service'
4 |
5 | export interface LoadingMaskServiceOption {
6 | message?: string,
7 | loadingType?: string,
8 | background?: string,
9 | unlock?: boolean,
10 | }
11 |
12 | export interface LoadingMaskServiceFormatOption extends LoadingMaskServiceOption {
13 | close: () => void
14 | }
15 |
16 | export const useLoadingMask = createUseService({
17 | name: 'loading',
18 | optionsCallName:'$loadingMask',
19 | managerComponent: createDefaultManager('pl-loading-mask-manager', Service),
20 | createService: (getManager) => {
21 | return (option: LoadingMaskServiceOption): LoadingMaskServiceFormatOption => {
22 | option = option || {};
23 | getManager().then(manager => manager.service(option as LoadingMaskServiceFormatOption));
24 | return option as LoadingMaskServiceFormatOption
25 | }
26 | }
27 | },
28 | )
29 |
30 | export const $$loadingMask = createServiceWithoutContext(useLoadingMask)
31 |
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/edit/plc-color-picker.tsx:
--------------------------------------------------------------------------------
1 | import PlColorButton from "../../../PlColorButton";
2 | import PlColorPicker from "../../../PlColorPicker";
3 |
4 | import {designComponent} from "plain-ui-composition";
5 | import {PlcEmitsOptions, PlcPropsOptions} from "../utils/plc.utils";
6 | import {PlcScopeSlotsOptions} from "../utils/plc.scope-slots";
7 | import {useExternalPlc} from "../core/useExternalPlc";
8 |
9 | export default designComponent({
10 | name: 'plc-color-picker',
11 | props: {
12 | ...PlcPropsOptions
13 | },
14 | scopeSlots: PlcScopeSlotsOptions,
15 | emits: PlcEmitsOptions,
16 | setup({props, slots, scopeSlots, event}) {
17 | return useExternalPlc({
18 | props, scopeSlots, event, slots, defaultScopeSlots: {
19 | normal: ({row, plc}) => {
20 | return !!plc.props.field && <>
21 |
22 | {row[plc.props.field]}
23 | >
24 | },
25 | edit: ({row, plc}) => !plc.props.field ? null :
26 | }
27 | })
28 | },
29 | })
30 |
--------------------------------------------------------------------------------
/src/packages/PlFilter/editor/FilterTextContains.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, PropType, reactive, useModel} from "plain-ui-composition";
2 |
3 | import PlInput from "../../PlInput";
4 |
5 | export const FilterTextContains = designComponent({
6 | props: {
7 | modelValue: {type: Array as PropType},
8 | },
9 | emits: {
10 | onUpdateModelValue: (val?: any[]) => true,
11 | onEnter: () => true,
12 | },
13 | setup({props, event: {emit}}) {
14 |
15 | const model = useModel(() => props.modelValue, emit.onUpdateModelValue, {
16 | onChange: val => {
17 | state.text = (val || []).join(',')
18 | }
19 | })
20 |
21 | const state = reactive({
22 | text: (model.value || []).join(',')
23 | })
24 |
25 | const handler = {
26 | onChange: () => {
27 | if (state.text === (model.value || []).join(',')) {
28 | return
29 | } else {
30 | model.value = state.text.split(/[,,]/g).filter(i => !!(i.trim()))
31 | }
32 | }
33 | }
34 |
35 | return () =>
36 | },
37 | })
38 |
--------------------------------------------------------------------------------
/src/packages/PlProgressCircle/progress-circle.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-progress-circle {
3 | position: relative;
4 | display: inline-block;
5 | vertical-align: middle;
6 | border-radius: 1000px;
7 |
8 | & > svg > path {
9 | transform-origin: center center;
10 | }
11 |
12 | .pl-progress-circle-label {
13 | height: 100%;
14 | width: 100%;
15 | position: absolute;
16 | top: 0;
17 | left: 0;
18 | display: flex;
19 | align-items: center;
20 | justify-content: center;
21 |
22 | .pl-icon {
23 | font-size: 24px;
24 | }
25 | }
26 |
27 | @keyframes path-animate {
28 | from {
29 | transform: rotate(0deg);
30 | }
31 | to {
32 | transform: rotate(360deg);
33 | }
34 | }
35 |
36 | &.pl-progress-circle-loading {
37 | & > svg {
38 | animation: path-animate 15000ms linear infinite;
39 |
40 | & > path {
41 | animation: path-animate 1500ms linear infinite;
42 | }
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/vue3-essential',
8 | 'eslint:recommended',
9 | '@vue/typescript/recommended'
10 | ],
11 | parserOptions: {
12 | ecmaVersion: 2020
13 | },
14 | rules: {
15 | 'no-console': 'off',
16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
17 | '@typescript-eslint/no-non-null-assertion': 'off',
18 | '@typescript-eslint/no-explicit-any': 'off',
19 | '@typescript-eslint/no-use-before-define': 'off',
20 | '@typescript-eslint/member-delimiter-style': 'off',
21 | 'no-extra-boolean-cast': 'off',
22 | '@typescript-eslint/no-unused-vars': 'off',
23 | '@typescript-eslint/no-var-requires': 'off',
24 | '@typescript-eslint/ban-ts-ignore': 'off',
25 | '@typescript-eslint/explicit-module-boundary-types': 'off',
26 | '@typescript-eslint/prefer-as-const': 'off',
27 | '@typescript-eslint/ban-ts-comment': 'off',
28 | '@typescript-eslint/no-empty-function': 'off',
29 | '@typescript-eslint/no-namespace': 'off',
30 | 'prefer-const': 'off',
31 | 'no-case-declarations': 'off',
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/packages/PlcTextarea/PlTextareaDialog.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, useModel} from "plain-ui-composition";
2 |
3 | import {EditProps, useEdit} from "../../use/useEdit";
4 | import PlInput from "../PlInput";
5 | import useDialog from "../useDialog";
6 |
7 | export const PlTextareDialog = designComponent({
8 | name: 'pl-textarea-dialog',
9 | props: {
10 | ...EditProps,
11 | modelValue: {type: [String, Number] as any},
12 | },
13 | emits: {
14 | onUpdateModelValue: (val?: any) => true,
15 | },
16 | setup({props, event: {emit}}) {
17 |
18 | const $dialog = useDialog()
19 | const {editComputed} = useEdit()
20 |
21 | const model = useModel(() => props.modelValue, emit.onUpdateModelValue)
22 |
23 | const onClick = () => {
24 | if (!editComputed.value.editable) {return}
25 | $dialog({
26 | editType: 'textarea',
27 | editValue: model.value,
28 | confirmButton: true,
29 | cancelButton: true,
30 | onConfirm: (val) => {
31 | model.value = val
32 | }
33 | })
34 | }
35 |
36 | return () =>
37 | },
38 | })
39 |
--------------------------------------------------------------------------------
/story/pages/data/mock.js:
--------------------------------------------------------------------------------
1 | /*
2 | const fs = require('fs')
3 | const path = require('path')
4 | const mock = require('mockjs').mock
5 |
6 | function generate() {
7 | [
8 | {file: 'data.json', num: 2000},
9 | {file: 'data-1.json', num: 50},
10 | {file: 'data-2.json', num: 200},
11 | ].forEach((item) => {
12 | fs.writeFileSync(path.resolve(__dirname, `./${item.file}`), JSON.stringify(
13 | (mock({
14 | [`array|${item.num}`]: [
15 | {
16 | "id|+1": 0,
17 | color: '@color',
18 | name: '@first',
19 | date: '@date',
20 | "star|0-10": 5,
21 | "flag": () => Math.random() > 0.5 ? 'Y' : 'N',
22 | "size|40-80": 60,
23 | addr: '@county(true)',
24 | url: '@url',
25 | domain: '@domain',
26 | protocol: '@protocol',
27 | email: '@email',
28 | ip: '@ip',
29 | }
30 | ]
31 | })).array,
32 | null,
33 | 2,
34 | ))
35 | })
36 | }
37 |
38 | generate()*/
39 |
--------------------------------------------------------------------------------
/src/packages/PlIcon/icons/el-icon-code.json:
--------------------------------------------------------------------------------
1 | [""]
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/standard/plc-index.tsx:
--------------------------------------------------------------------------------
1 |
2 | import {designComponent} from "plain-ui-composition";
3 | import {createPlcPropOptions, PlcEmitsOptions} from "../utils/plc.utils";
4 | import {PlcScopeSlotsOptions} from "../utils/plc.scope-slots";
5 | import {useExternalPlc} from "../core/useExternalPlc";
6 |
7 | export default designComponent({
8 | name: 'plc-index',
9 | props: {
10 | ...createPlcPropOptions({
11 | autoFixedLeft: true,
12 | order: -9995,
13 | width: 45,
14 | align: 'center',
15 | noPadding: true,
16 | hideInForm: true,
17 | }),
18 | summaryText: {type: String, default: '合计'},
19 | start: {type: Number, default: 0},
20 | },
21 | scopeSlots: {
22 | ...PlcScopeSlotsOptions,
23 | },
24 | emits: {
25 | ...PlcEmitsOptions,
26 | },
27 | setup({props, slots, scopeSlots, event}) {
28 | return useExternalPlc({
29 | props, scopeSlots, event, slots, defaultScopeSlots: {
30 | head: () => '#',
31 | normal: ({node, plc}) => (plc.props as any).start + node.index + 1,
32 | summary: () => {props.summaryText},
33 | }
34 | })
35 | },
36 | })
37 |
--------------------------------------------------------------------------------
/src/packages/PlTable/plc/utils/plc.scope-slots.tsx:
--------------------------------------------------------------------------------
1 | import {TableRenderScope, tPlcType} from "./plc.type";
2 | import {VueNode} from "plain-ui-composition";
3 |
4 | export type PlcRenderFunction = (scope: TableRenderScope) => VueNode
5 | export type PlcPropsHead = (scope: { plc: tPlcType }) => VueNode
6 | export type PlcPropsDefault = PlcRenderFunction
7 | export type PlcPropsSummary = PlcRenderFunction
8 | export type PlcPropsEdit = PlcRenderFunction
9 | export type PlcPropsForm = PlcRenderFunction
10 |
11 | export type ScopeSlotsType any>> = { [k in keyof ScopeSlots]: ((scope: Parameters[0], defaultNode?: VueNode) => VueNode) & { isExist: () => boolean } }
12 |
13 | export const PlcScopeSlotsOptions = {
14 | head: {} as PlcPropsHead, // 列标题渲染函数
15 | normal: {} as PlcPropsDefault, // 列内容默认渲染函数
16 | summary: {} as PlcPropsSummary, // 列内容在合计行上的渲染函数
17 | edit: {} as PlcPropsEdit, // 列内容在编辑状态下的渲染函数
18 | form: {} as PlcPropsForm, // 表单编辑时渲染的内容
19 | }
20 |
21 | export type tPlcScopeSlots = ScopeSlotsType
22 |
23 | export type tPlcSlots = Record VueNode) & { isExist: () => boolean }>
24 |
--------------------------------------------------------------------------------
/src/utils/createAnimate.ts:
--------------------------------------------------------------------------------
1 | export function createAnimate(
2 | {
3 | initValue,
4 | time,
5 | action,
6 | }: {
7 | initValue: () => number,
8 | time: number,
9 | action: (val: number) => void,
10 | }) {
11 |
12 | let cancel = null as null | number
13 |
14 | return {
15 | start: (percent: number, done?: () => void) => {
16 | if (cancel != null) {
17 | cancelAnimationFrame(cancel)
18 | }
19 | let startTime = Date.now()
20 |
21 | let n = initValue()
22 | let k = (percent - n) / time
23 |
24 | const run = () => {
25 | let nowTime = Date.now()
26 | let deltaTime = nowTime - startTime
27 |
28 | if (deltaTime > time) {
29 | cancel = null
30 | action(percent)
31 | !!done && done()
32 | return
33 | }
34 | action(Number((deltaTime * k + n).toFixed(2)))
35 | cancel = requestAnimationFrame(run)
36 | }
37 | run()
38 | },
39 | stop: () => {
40 | if (cancel != null) {
41 | cancelAnimationFrame(cancel)
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/packages/createUseTableOption/use/use.cache.utils.tsx:
--------------------------------------------------------------------------------
1 | import {tPlc, tPlcType} from "../../PlTable/plc/utils/plc.type";
2 |
3 | export interface iTableOptionCacheItemData {
4 | id: number,
5 | title: string,
6 | time: string,
7 | data: Record,
8 | }
9 |
10 | export interface iTableOptionCacheData {
11 | tableId: string,
12 | activeId: number | undefined,
13 | data: iTableOptionCacheItemData[],
14 | }
15 |
16 | export interface iTableOptionApplyCacheParam {
17 | plcList: tPlc[],
18 | cacheData: CacheData | undefined
19 | sourceList: tPlcType[],
20 | }
21 |
22 | export interface iTableOptionGetCacheParam {
23 | plcList: tPlc[],
24 | sourceList: tPlcType[],
25 | }
26 |
27 | export interface iTableOptionCacheRegistryConfig {
28 | cacheKey: string,
29 | applyCache: (param: iTableOptionApplyCacheParam) => void,
30 | getCache: (param: iTableOptionGetCacheParam) => CacheData,
31 | }
32 |
33 | export const getTableId = (plcTypeList: tPlcType[]): string => {
34 | return plcTypeList.map(i => {
35 | if (i.group) {
36 | return `${i.props.title || '#'}-${getTableId(i.children)}`
37 | } else {
38 | return `${i.props.title || '#'}-${i.props.field || '@'}`
39 | }
40 | }).join('_')
41 | }
42 |
--------------------------------------------------------------------------------
/src/packages/PlTabs/header/horizontal/tabs-header-text.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-tabs-header.pl-tabs-header-horizontal.pl-tabs-header-type-text {
3 |
4 | .pl-tabs-header-item {
5 | line-height: 45px;
6 |
7 | & + .pl-tabs-header-item {
8 | margin-left: 32px;
9 | }
10 |
11 | &.pl-tabs-header-item-active, &:hover {
12 | color: $colorPrimary;
13 | }
14 | }
15 |
16 | .pl-tabs-header-list {
17 | position: relative;
18 |
19 | .pl-tabs-header-horizontal-text-indicator-container {
20 | background-color: $disabled;
21 | position: absolute;
22 | left: 0;
23 | right: 0;
24 | height: 2px;
25 |
26 | .pl-tabs-header-horizontal-text-indicator {
27 | position: absolute;
28 | width: 0;
29 | left: 0;
30 | height: 100%;
31 | background-color: $colorPrimary;
32 | display: block;
33 | transition: $transition 150ms all;
34 | }
35 | }
36 | }
37 |
38 | &.pl-tabs-header-pos-top {
39 | .pl-tabs-header-horizontal-text-indicator-container {
40 | bottom: 0;
41 | }
42 | }
43 |
44 | &.pl-tabs-header-pos-bottom {
45 | .pl-tabs-header-horizontal-text-indicator-container {
46 | top: 0;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/packages/PlDropdownGroup/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent, useClasses, useRefs} from "plain-ui-composition";
2 |
3 | export const PlDropdownGroup = designComponent({
4 | name: 'pl-dropdown-group',
5 | props: {
6 | title: {type: String}
7 | },
8 | slots: ['default', 'head'],
9 | inheritPropsType: HTMLDivElement,
10 | setup({props, slots}) {
11 |
12 | const {refs, onRef} = useRefs({el: HTMLDivElement})
13 |
14 | const classes = useClasses(() => [
15 | 'pl-dropdown-group',
16 | {'pl-dropdown-no-title': !slots.head.isExist() && !props.title}
17 | ])
18 |
19 | return {
20 | refer: {
21 | refs,
22 | },
23 | render: () => (
24 |
25 | {slots.head.isExist() || props.title && (
26 |
27 | {slots.head(props.title)}
28 |
29 | )}
30 |
31 | {slots.default()}
32 |
33 |
34 | )
35 | }
36 | },
37 | })
38 |
39 | export default PlDropdownGroup
40 |
--------------------------------------------------------------------------------
/src/packages/PlcObject/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent} from "plain-ui-composition";
2 | import PlObject, {PlObjectPropsOption} from "../PlObject";
3 | import {PlcEmitsOptions, PlcPropsOptions} from "../PlTable/plc/utils/plc.utils";
4 | import {PlcScopeSlotsOptions} from "../PlTable/plc/utils/plc.scope-slots";
5 | import {useExternalPlc} from "../PlTable/plc/core/useExternalPlc";
6 |
7 |
8 | export const PlcObject = designComponent({
9 | name: 'plc-object',
10 | props: {
11 | ...PlcPropsOptions,
12 | ...PlObjectPropsOption,
13 | },
14 | scopeSlots: PlcScopeSlotsOptions,
15 | emits: PlcEmitsOptions,
16 | setup({props, slots, scopeSlots, event}) {
17 | return useExternalPlc({
18 | props, scopeSlots, slots, event, defaultScopeSlots: {
19 | edit: ({row, plc}) => !plc.props.field ? null :
20 | {
22 | prev[key] = (props as any)[key]
23 | return prev
24 | }, {} as Record)}
25 | showKey={plc.props.field}
26 | row={row}
27 | />
28 | }
29 | })
30 | },
31 | })
32 |
33 | export default PlcObject
34 |
--------------------------------------------------------------------------------
/src/utils/findReactElement.tsx:
--------------------------------------------------------------------------------
1 | import {VueNode} from "plain-ui-composition";
2 | import {isVNode} from 'vue'
3 |
4 | export function findReactElement(node: VueNode, isMatch: (node: VueNode) => boolean): VueNode[] | null {
5 |
6 | if (node == null) {return null}
7 | const type = typeof node
8 | switch (type) {
9 | case "number":
10 | case "string":
11 | case "boolean":
12 | return null
13 | }
14 |
15 | if (Array.isArray(node) && node.length > 0) {
16 | const ret: VueNode[] = []
17 | node.forEach(n => {
18 | const findList = findReactElement(n, isMatch)
19 | if (!!findList && findList.length > 0) {
20 | ret.push(...findList)
21 | }
22 | })
23 | return ret
24 | }
25 |
26 | if (isVNode(node)) {
27 | if (isMatch(node)) {
28 | return [node]
29 | }
30 | if (node.children && Array.isArray(node.children)) {
31 | const ret: VueNode[] = []
32 | node.children.forEach(n => {
33 | const findList = findReactElement(n, isMatch)
34 | if (!!findList && findList.length > 0) {
35 | ret.push(...findList)
36 | }
37 | })
38 | return ret
39 | }
40 | }
41 |
42 | return null
43 | }
44 |
--------------------------------------------------------------------------------
/story/pages/data/mock.database.js:
--------------------------------------------------------------------------------
1 | /*
2 | const fs = require('fs')
3 | const path = require('path')
4 | const mock = require('mockjs').mock
5 |
6 | const createGeneratoe = (options) => {
7 | const {length} = options
8 | return () => {
9 | return options[Math.floor(Math.random() * length)]
10 | }
11 | }
12 |
13 | const nextFlag = createGeneratoe(['Y', 'N'])
14 | const nextSelect = createGeneratoe(['potential', 'store', 'consumer'])
15 |
16 | function generate() {
17 | const data = (mock({
18 | [`array|${100}`]: [
19 | {
20 | id: '@guid',
21 | created_at: '@datetime',
22 | created_by: '@first',
23 | updated_at: '@datetime',
24 | updated_by: '@first',
25 | normal_text: '@first',
26 | long_text: '@paragraph',
27 | 'number_val|1-100': 100,
28 | flag: nextFlag,
29 | select_val: nextSelect,
30 | color_val: '@color',
31 | date_val: '@datetime',
32 | time_val: '@time',
33 | parent_id: '',
34 | image_id: '',
35 | }
36 | ]
37 | })).array;
38 |
39 | fs.writeFileSync(path.resolve(__dirname, `./demo.json`), JSON.stringify(data, null, 2,))
40 | }
41 |
42 | generate()
43 | */
44 |
--------------------------------------------------------------------------------
/src/packages/PlInputGroup/input-group.scss:
--------------------------------------------------------------------------------
1 | .pl-input-group {
2 | display: inline-block;
3 | white-space: nowrap;
4 |
5 | & > * {
6 | display: inline-block;
7 |
8 | &.pl-input-group.pl-input-group-block {
9 | display: inline-flex;
10 | }
11 | }
12 |
13 | &.pl-input-group-block {
14 | width: 100%;
15 | display: flex;
16 | align-items: center;
17 | flex-wrap: nowrap;
18 | }
19 |
20 | & > *:not(:first-child) {
21 | border-top-left-radius: 0 !important;
22 | border-bottom-left-radius: 0 !important;
23 |
24 | input, .pl-input-inner {
25 | border-top-left-radius: 0 !important;
26 | border-bottom-left-radius: 0 !important;
27 | }
28 | }
29 |
30 | & > *:not(:last-child) {
31 | border-top-right-radius: 0 !important;
32 | border-bottom-right-radius: 0 !important;
33 |
34 | input, .pl-input-inner {
35 | border-top-right-radius: 0 !important;
36 | border-bottom-right-radius: 0 !important;
37 | }
38 | }
39 |
40 | & > .pl-button {
41 | position: relative;
42 | z-index: 2;
43 | }
44 |
45 | & > *, & > * input {
46 | &:hover, &:focus, &.pl-input-focus {
47 | position: relative;
48 | z-index: 1;
49 | }
50 | }
51 |
52 | @for $i from 2 through 10 {
53 | & > *:nth-child(#{$i}) {
54 | margin-left: -#{$i - 1}px;
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/packages/PlBadge/badge.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-badge {
3 | @include public-style;
4 | display: inline-block;
5 | vertical-align: baseline;
6 | position: relative;
7 |
8 | .pl-badge-content {
9 | position: absolute;
10 | font-size: 12px;
11 | padding: 2px 4px;
12 | border: solid 1px white;
13 |
14 | @include statusMixin(badge-content) {
15 | background-color: $value;
16 | border-radius: 100px;
17 | color: white;
18 | }
19 |
20 | &.pl-badge-content-top {
21 | top: 0;
22 |
23 | &.pl-badge-content-start {
24 | transform: translate(-50%, -50%);
25 | }
26 |
27 | &.pl-badge-content-end {
28 | transform: translate(50%, -50%);
29 | }
30 | }
31 |
32 | &.pl-badge-content-bottom {
33 | bottom: 0;
34 |
35 | &.pl-badge-content-start {
36 | transform: translate(-50%, 50%);
37 | }
38 |
39 | &.pl-badge-content-end {
40 | transform: translate(50%, 50%);
41 | }
42 | }
43 |
44 | &.pl-badge-content-start {
45 | left: 0;
46 | }
47 |
48 | &.pl-badge-content-end {
49 | right: 0;
50 | }
51 |
52 | &.pl-badge-content-dot {
53 | height: 12px;
54 | width: 12px;
55 | padding: 0;
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/packages/PlTime/panel/time-base-column.scss:
--------------------------------------------------------------------------------
1 | @import "./time.public";
2 |
3 | @include theme {
4 | .pl-time-base-column {
5 | display: inline-block;
6 | height: 7*$time-size;
7 | width: 40px;
8 | border-radius: 2px;
9 |
10 | .pl-time-base-column-list {
11 | margin: 0;
12 | padding: 0;
13 | list-style: none;
14 | position: relative;
15 |
16 | .pl-time-base-column-item {
17 | font-size: 12px;
18 | height: $time-size;
19 | line-height: $time-size;
20 | transition: all 300ms $transition;
21 | text-align: center;
22 |
23 | &.pl-time-base-column-option-item {
24 | cursor: pointer;
25 |
26 | &:hover {
27 | background-color: rgba($colorInfo, 0.1);
28 | color: $ihc;
29 | }
30 | }
31 |
32 | &.pl-time-base-column-item-disabled {
33 | color: $disabledText;
34 | cursor: not-allowed;
35 |
36 | &:hover {
37 | color: $disabledText;
38 | background-color: transparent;
39 | }
40 | }
41 |
42 | &.pl-time-base-column-item-current {
43 | background-color: rgba($colorPrimary, 0.1);
44 | color: $colorPrimary;
45 |
46 | &:hover {
47 | color: $colorPrimary;
48 | }
49 | }
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/packages/PlColorPicker/sub/color-sv-panel.scss:
--------------------------------------------------------------------------------
1 | @include theme {
2 | .pl-color-sv-panel {
3 | position: relative;
4 | box-shadow: 0 0px 4px 0 rgba(0, 0, 0, 0.4);
5 | cursor: pointer;
6 | display: inline-block;
7 |
8 | &:before, &:after, & span {
9 | pointer-events: none;
10 | }
11 |
12 | &, &:before, &:after {
13 | border-radius: 2px;
14 | }
15 |
16 | &:before, &:after {
17 | position: absolute;
18 | top: 0;
19 | left: 0;
20 | right: 0;
21 | bottom: 0;
22 | content: '';
23 | }
24 |
25 | &:before {
26 | background: linear-gradient(to right, white, rgba(255, 255, 255, 0));
27 | }
28 |
29 | &:after {
30 | background: linear-gradient(to top, black, rgba(255, 255, 255, 0));
31 | }
32 |
33 | .pl-color-sv-thumb {
34 | display: inline-block;
35 | position: absolute;
36 | width: 1px;
37 | height: 1px;
38 | left: 0;
39 | top: 0;
40 | z-index: 1;
41 |
42 | &:after {
43 | display: inline-block;
44 | width: 10px;
45 | height: 10px;
46 | border: solid 1px #999;
47 | border-radius: 10px;
48 | position: absolute;
49 | top: -5px;
50 | left: -5px;
51 | content: '';
52 | box-sizing: border-box;
53 | background-color: white;
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/packages/PlCheckboxInner/index.tsx:
--------------------------------------------------------------------------------
1 | import {designComponent} from "plain-ui-composition"
2 | import {useClasses} from "plain-ui-composition";
3 | import {SVGAttributes} from 'vue'
4 |
5 | import {CheckboxStatus} from "../../utils/constant";
6 | import './checkbox-inner.scss'
7 |
8 | export const PlCheckboxInner = designComponent({
9 | name: 'pl-checkbox-inner',
10 | props: {
11 | disabled: {type: Boolean},
12 | checkStatus: {type: String},
13 | },
14 | inheritPropsType: {} as SVGAttributes,
15 | setup({props}) {
16 |
17 | const classes = useClasses(() => [
18 | 'pl-checkbox-inner',
19 | `pl-checkbox-inner-status-${props.checkStatus}`,
20 | {
21 | 'pl-checkbox-inner-disabled': props.disabled,
22 | }
23 | ])
24 |
25 | return {
26 | render: () => (
27 |
31 | )
32 | }
33 | },
34 | })
35 |
36 | export default PlCheckboxInner
37 |
--------------------------------------------------------------------------------
/src/packages/PlRoot/createDefaultService.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 创建一个默认的root service组件
3 | * 这个组件最后返回的refer,必须含有isShow、isOpen、service三个属性。
4 | * isShow:当前是否已经显示,因为存在过渡动画,所以如果值为true,但是此时在页面可能没有完全显示。值为false,可能在页面上没有完全隐藏;
5 | * isOpen:当前是否已经在页面上完全打开或者消失。值为true,表示当前已经完全显示,但是此时isShow可能为false。因为隐藏过渡动画没有结束的时候就是这种情况、同理isOpen为true的时候也是这样。
6 | * 只有当isShow以及isOpen都是false的情况下,这个service才是处于闲置可以服用的状态。
7 | *
8 | * @author 韦胜健
9 | * @date 2020/11/26 9:24
10 | */
11 | import {designComponent, VueNode} from "plain-ui-composition";
12 |
13 | export function createDefaultService