合并冲突
This commit is contained in:
commit
30d4f31265
|
@ -11,6 +11,7 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
*.lock
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
|
@ -20,3 +21,4 @@ dist-ssr
|
|||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "Wscats.vue"
|
||||
}
|
||||
}
|
|
@ -4,5 +4,7 @@
|
|||
],
|
||||
"version": "0.0.0",
|
||||
"useWorkspaces": true,
|
||||
"npmClient": "yarn"
|
||||
"npmClient": "yarn",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useNx": false
|
||||
}
|
37
package.json
37
package.json
|
@ -18,13 +18,46 @@
|
|||
"eslint-plugin-vue": "^9.4.0",
|
||||
"husky": "^8.0.0",
|
||||
"intersection-observer": "^0.12.2",
|
||||
"lerna": "^4.0.0",
|
||||
"lerna": "^5.5.4",
|
||||
"lint-staged": "^13.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"stylelint": "^14.11.0",
|
||||
"stylelint-config-recommended-scss": "^7.0.0",
|
||||
"stylelint-config-standard": "^28.0.0",
|
||||
"stylelint-scss": "^3.3.1"
|
||||
"stylelint-scss": "^3.3.1",
|
||||
"@vitejs/plugin-vue": "^3.1.0",
|
||||
"@vitejs/plugin-vue-jsx": "^2.0.1",
|
||||
"@vue/babel-plugin-jsx": "^1.1.1",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.1.0",
|
||||
"vue-tsc": "^0.40.4",
|
||||
"@vue/test-utils": "^2.0.0",
|
||||
"@babel/parser": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@babel/preset-typescript": "^7.18.0",
|
||||
"@babel/traverse": "^7.19.0",
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/commander": "^2.12.2",
|
||||
"@types/ora": "^3.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.37.0",
|
||||
"@typescript-eslint/parser": "^5.37.0",
|
||||
"@vue/compiler-sfc": "^3.2.0",
|
||||
"@vuedx/typecheck": "^0.7.5",
|
||||
"@vuedx/typescript-plugin-vue": "^0.7.5",
|
||||
"babel-jest": "^29.0.3",
|
||||
"chalk": "^5.0.0",
|
||||
"commander": "^9.4.0",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"inquirer": "^9.1.1",
|
||||
"jest": "^29.0.0",
|
||||
"ora": "^6.1.2",
|
||||
"patch-vue-directive-ssr": "^0.0.1",
|
||||
"sass": "^1.32.2",
|
||||
"shelljs": "^0.8.4",
|
||||
"vite-plugin-md": "^0.20.0",
|
||||
"vite-svg-loader": "^3.6.0",
|
||||
"vitepress": "0.20.1",
|
||||
"vitepress-theme-demoblock": "1.3.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
"packages/docs-vue/{*.vue,*.js,*.ts,*.jsx,*.tsx}": "eslint --fix",
|
||||
|
|
Binary file not shown.
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
// This starter template is using Vue 3 <script setup> SFCs
|
||||
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
import HelloWorld from './components/HelloWorld.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>();
|
||||
|
||||
const count = ref(0)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>();
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
in your IDE for a better DX
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,8 @@
|
|||
/// <reference types="vite/client" />
|
||||
// / <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
import type { DefineComponent } from 'vue';
|
||||
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -1,15 +0,0 @@
|
|||
import type { App } from 'vue';
|
||||
import According from './src/according.component';
|
||||
import AccordingItem from './src/components/according-item.component';
|
||||
|
||||
export * from './src/according.props';
|
||||
export * from './src/components/according-item.props';
|
||||
|
||||
export { According, AccordingItem };
|
||||
|
||||
export default {
|
||||
install(app: App): void {
|
||||
app.component(According.name, According);
|
||||
app.component(AccordingItem.name, AccordingItem);
|
||||
}
|
||||
};
|
|
@ -11,15 +11,28 @@ export default defineComponent({
|
|||
setup(props: AccordingProps, context: SetupContext) {
|
||||
const accordingStyle = computed(() => ({
|
||||
height: props.height ? `${props.height}px` : '',
|
||||
width: props.width ? `${props.width}px` : '',
|
||||
width: props.width ? `${props.width}px` : ''
|
||||
}));
|
||||
|
||||
const accordingClass = computed(() => {
|
||||
const customClassArray = props.customClass;
|
||||
const accordingClassObject = {
|
||||
'farris-panel': true,
|
||||
according: true
|
||||
};
|
||||
customClassArray.reduce<Record<string, unknown>>((classObject, classString) => {
|
||||
classObject[classString] = true;
|
||||
return classObject;
|
||||
}, accordingClassObject);
|
||||
return accordingClassObject;
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={`farris-panel accoriding ${props.customClass.join(' ')}`} style={accordingStyle.value}>
|
||||
<div class={accordingClass.value} style={accordingStyle.value}>
|
||||
{context.slots.default && context.slots.default()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* 很重要 */
|
||||
.farris-panel {
|
||||
border: 1px solid rgba(0, 0, 0, .125)
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { ExtractPropTypes } from 'vue';
|
||||
|
||||
export const accordingItemProps = {
|
||||
width:{type:Number},
|
||||
height:{type:Number},
|
||||
title:{type:String,default:''},
|
||||
disable:{type:Boolean,default:false}
|
||||
width: { type: Number },
|
||||
height: { type: Number },
|
||||
title: { type: String, default: '' },
|
||||
disable: { type: Boolean, default: false }
|
||||
};
|
||||
export type AccordingItemProps = ExtractPropTypes<typeof accordingItemProps>;
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import type { App } from 'vue';
|
||||
import Accordion from './src/accordion.component';
|
||||
import AccordionItem from './src/components/accordion-item.component';
|
||||
|
||||
export * from './src/accordion.props';
|
||||
export * from './src/components/accordion-item.props';
|
||||
|
||||
export { Accordion, AccordionItem };
|
||||
|
||||
export default {
|
||||
install(app: App): void {
|
||||
app.component(Accordion.name, Accordion);
|
||||
app.component(AccordionItem.name, AccordionItem);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
import { isContext } from 'vm';
|
||||
import { computed, defineComponent, SetupContext } from 'vue';
|
||||
import { AccordionProps, accordionProps } from './accordion.props';
|
||||
|
||||
import './accordion.css';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FAccordion',
|
||||
props: accordionProps,
|
||||
emits: [],
|
||||
setup(props: AccordionProps, context: SetupContext) {
|
||||
const accordionStyle = computed(() => ({
|
||||
height: props.height ? `${props.height}px` : '',
|
||||
width: props.width ? `${props.width}px` : ''
|
||||
}));
|
||||
|
||||
const accordionClass = computed(() => {
|
||||
const customClassArray = props.customClass;
|
||||
const accordionClassObject = {
|
||||
'farris-panel': true,
|
||||
according: true
|
||||
};
|
||||
customClassArray.reduce<Record<string, unknown>>((classObject, classString) => {
|
||||
classObject[classString] = true;
|
||||
return classObject;
|
||||
}, accordionClassObject);
|
||||
return accordionClassObject;
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={accordionClass.value} style={accordionStyle.value}>
|
||||
{context.slots.default && context.slots.default()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
/* 很重要 */
|
||||
.farris-panel {
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import { ExtractPropTypes } from 'vue';
|
||||
|
||||
export const accordingProps = {
|
||||
export const accordionProps = {
|
||||
customClass: { type: Array<string>, default: [] },
|
||||
height: { type: Number },
|
||||
width: { type: Number },
|
||||
enableFold: { type: Boolean, default: true },
|
||||
expanded: { type: Boolean, default: false },
|
||||
expanded: { type: Boolean, default: false }
|
||||
};
|
||||
export type AccordingProps = ExtractPropTypes<typeof accordingProps>;
|
||||
export type AccordionProps = ExtractPropTypes<typeof accordionProps>;
|
|
@ -0,0 +1,25 @@
|
|||
.card-header {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.panel-item-title {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.panel-item-tool {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.panel-item-clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.f-state-disable {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 很重要 */
|
||||
.card {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
|
@ -1,25 +1,28 @@
|
|||
import { computed, defineComponent, ref, SetupContext } from 'vue';
|
||||
import { accordionItemProps, AccordionItemProps } from './accordion-item.props';
|
||||
|
||||
import './accordion-item.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FAccordingItem',
|
||||
props: {},
|
||||
name: 'FAccordionItem',
|
||||
props: accordionItemProps,
|
||||
emits: [],
|
||||
setup(props, context: SetupContext) {
|
||||
const title = ref('');
|
||||
setup(props: AccordionItemProps, context: SetupContext) {
|
||||
const title = ref(props.title);
|
||||
const isActive = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
|
||||
function selectAccordingItem() {}
|
||||
function selectAccordionItem() {}
|
||||
|
||||
function onClick($event: Event) {
|
||||
selectAccordingItem();
|
||||
selectAccordionItem();
|
||||
}
|
||||
|
||||
const accordingItemClass = computed(() => ({
|
||||
const accordionItemClass = computed(() => ({
|
||||
'f-state-disable': isDisabled.value,
|
||||
card: true,
|
||||
'farris-panel-item': true,
|
||||
'f-state-selected': isActive.value,
|
||||
'f-state-selected': isActive.value
|
||||
}));
|
||||
|
||||
const shouldShowHeader = computed(() => {
|
||||
|
@ -32,13 +35,13 @@ export default defineComponent({
|
|||
|
||||
const headIconClass = computed(() => ({
|
||||
'f-icon': true,
|
||||
'f-according-collapse': !isActive.value,
|
||||
'f-according-expand': isActive.value,
|
||||
'f-accordion-collapse': !isActive.value,
|
||||
'f-accordion-expand': isActive.value
|
||||
}));
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={accordingItemClass.value}>
|
||||
<div class={accordionItemClass.value}>
|
||||
<div class="card-header" onClick={onClick}>
|
||||
<div class="panel-item-title">
|
||||
{shouldShowHeader.value && <span>{title.value}</span>}
|
||||
|
@ -48,11 +51,11 @@ export default defineComponent({
|
|||
<div class="panel-item-tool">{context.slots.toolbar && context.slots.toolbar()}</div>
|
||||
<div class="panel-item-clear"></div>
|
||||
</div>
|
||||
<div dropAnimation="active?'active':'inactive'">
|
||||
<div>
|
||||
<div class="card-body">{context.slots.content && context.slots.content()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import { ExtractPropTypes } from 'vue';
|
||||
|
||||
export const accordionItemProps = {
|
||||
width: { type: Number },
|
||||
height: { type: Number },
|
||||
title: { type: String, default: '' },
|
||||
disable: { type: Boolean, default: false }
|
||||
};
|
||||
export type AccordionItemProps = ExtractPropTypes<typeof accordionItemProps>;
|
|
@ -0,0 +1,25 @@
|
|||
.card-header {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.panel-item-title {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.panel-item-tool {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.panel-item-clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.f-state-disable {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 很重要 */
|
||||
.card {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
import { defineComponent, computed, ref, SetupContext } from 'vue';
|
||||
import { avatarProps, AvatarProps } from './avatar.props';
|
||||
import { useImage } from './composition/use-image';
|
||||
|
||||
import './avatar.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Avatar',
|
||||
props: avatarProps,
|
||||
emits: ['change'],
|
||||
emits: ['change', 'update:modelValue'],
|
||||
setup(props: AvatarProps, context: SetupContext) {
|
||||
const avatarClass = computed(() => ({
|
||||
'f-avatar': true,
|
||||
|
@ -13,6 +16,8 @@ export default defineComponent({
|
|||
'f-avatar-square': props.shape === 'square'
|
||||
}));
|
||||
|
||||
const modelValue = ref(props.modelValue);
|
||||
|
||||
const avatarStyle = computed(() => ({
|
||||
width: props.avatarWidth + 'px',
|
||||
height: props.avatarHeight + 'px'
|
||||
|
@ -29,32 +34,30 @@ export default defineComponent({
|
|||
|
||||
function getfiledata() {}
|
||||
|
||||
const defaultImgSrc =
|
||||
'data:image/jpeg;base64,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';
|
||||
const errorImgSrc =
|
||||
'data:image/jpeg;base64,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';
|
||||
const file = ref(null);
|
||||
|
||||
const imageType = computed(() => props.type.join());
|
||||
const { acceptTypes, imageSource, onClickImage } = useImage(props, context, file, modelValue);
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={avatarClass.value} style={avatarStyle.value}>
|
||||
<div class={avatarClass.value} style={avatarStyle.value} onClick={onClickImage}>
|
||||
{showLoading && (
|
||||
<div class="f-avatar-upload-loading">
|
||||
<div class="loading-inner">加载中</div>
|
||||
</div>
|
||||
)}
|
||||
<img title={props.tile} class="f-avatar-image" src={imgSrc} onError={errorSrc()} />
|
||||
<img title={props.tile} class="f-avatar-image" src={imageSource.value} onError={errorSrc()} />
|
||||
{!props.readonly && (
|
||||
<div class="f-avatar-icon">
|
||||
<span class="f-icon f-icon-camera"></span>
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
ref="file"
|
||||
name="file-input"
|
||||
type="file"
|
||||
class="f-avatar-upload"
|
||||
accept={imageType.value}
|
||||
accept={acceptTypes.value}
|
||||
onChange={getfiledata}
|
||||
style="display: none;"
|
||||
/>
|
||||
|
|
|
@ -27,6 +27,10 @@ export const avatarProps = {
|
|||
* 头像最大尺寸, 单位MB
|
||||
*/
|
||||
maxSize: { type: Number, default: 1 },
|
||||
/**
|
||||
* 组件值
|
||||
*/
|
||||
modelValue: { type: String, default: '' },
|
||||
/**
|
||||
* 头像标题
|
||||
*/
|
||||
|
@ -34,7 +38,7 @@ export const avatarProps = {
|
|||
/**
|
||||
* 支持的头像类型
|
||||
*/
|
||||
type: { type: Array<string>, default: [] },
|
||||
type: { type: Array<string>, default: [] }
|
||||
};
|
||||
|
||||
export type AvatarProps = ExtractPropTypes<typeof avatarProps>;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
.f-avatar {
|
||||
position: relative;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
&.f-avatar-readonly {
|
||||
cursor: default;
|
||||
}
|
||||
&.f-avatar-circle {
|
||||
border-radius: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
&.f-avatar-square {
|
||||
border-radius: 0;
|
||||
}
|
||||
.f-avatar-image,
|
||||
.f-avatar-defult {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.f-avatar-icon {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
.f-icon {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
&.f-avatar-circle .f-avatar-icon {
|
||||
border-radius: 100%;
|
||||
}
|
||||
&.f-avatar-square .f-avatar-icon {
|
||||
border-radius: 0;
|
||||
}
|
||||
.f-avatar-upload-loading {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
.loading-inner {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
margin-top: -25px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.f-avatar-icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,11 @@ import { ComputedRef } from 'vue';
|
|||
export interface UseImage {
|
||||
acceptTypes: ComputedRef<string>;
|
||||
|
||||
imageSrc: ComputedRef<string>;
|
||||
imageSource: ComputedRef<string>;
|
||||
|
||||
imageTitle: ComputedRef<string>;
|
||||
|
||||
onClickImage: () => void;
|
||||
}
|
||||
|
||||
export interface ImageFile {
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { computed, SetupContext } from 'vue';
|
||||
import { vue } from '@vitejs/plugin-vue';
|
||||
import { computed, ref, Ref, SetupContext } from 'vue';
|
||||
import { AvatarProps } from '../avatar.props';
|
||||
import { UseImage } from './types';
|
||||
|
||||
export function useImage(props: AvatarProps, context: SetupContext, fileInput: HTMLInputElement): UseImage {
|
||||
const defaultImage = '';
|
||||
const errorImage = '';
|
||||
export function useImage(props: AvatarProps, context: SetupContext, fileInput: any, modelValue: Ref<string>): UseImage {
|
||||
const defaultImage =
|
||||
'data:image/jpeg;base64,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';
|
||||
const errorImage =
|
||||
'data:image/jpeg;base64,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';
|
||||
|
||||
const readonly = ref(props.readonly);
|
||||
|
||||
// 判断是否是图片路径
|
||||
function isUrl(url: string) {
|
||||
|
@ -39,33 +44,28 @@ export function useImage(props: AvatarProps, context: SetupContext, fileInput: H
|
|||
return imageTypesArray.join(',');
|
||||
});
|
||||
|
||||
const imageSrc = computed(() => {
|
||||
if (!props.cover) {
|
||||
return defaultImage;
|
||||
const imageSource = computed(() => {
|
||||
const image = modelValue.value || props.cover || defaultImage;
|
||||
if (isUrl(image) || isBase64Image(image)) {
|
||||
return image;
|
||||
}
|
||||
if (isUrl(props.cover)) {
|
||||
return props.cover;
|
||||
}
|
||||
if (isBase64Image(props.cover)) {
|
||||
return props.cover;
|
||||
}
|
||||
return appendBase64ImageHeader(props.cover);
|
||||
return appendBase64ImageHeader(image);
|
||||
});
|
||||
|
||||
const imageTitle = computed(() => {
|
||||
return props.readonly ? '' : props.tile;
|
||||
return readonly.value ? '' : props.tile;
|
||||
});
|
||||
|
||||
function onClickImage() {
|
||||
if (this.readonly) {
|
||||
if (readonly.value) {
|
||||
return;
|
||||
}
|
||||
fileInput.click();
|
||||
fileInput && fileInput.value && fileInput.value.click();
|
||||
}
|
||||
|
||||
function getImageFile() {
|
||||
return this.imgFileObj;
|
||||
}
|
||||
|
||||
return { acceptTypes, imageSrc, imageTitle };
|
||||
return { acceptTypes, imageSource, imageTitle, onClickImage };
|
||||
}
|
||||
|
|
|
@ -3,103 +3,104 @@ import { ButtonEditProps } from '../button-edit.props';
|
|||
import { UseTextBox } from './types';
|
||||
|
||||
export function useTextBox(props: ButtonEditProps, context: SetupContext, modelValue: Ref<string>, displayText: Ref<string>): UseTextBox {
|
||||
const textBoxTitle = computed(() => (props.enableTitle ? modelValue.value : ''));
|
||||
const textBoxTitle = computed(() => (props.enableTitle ? modelValue.value : ''));
|
||||
|
||||
const textBoxPlaceholder = computed(() => ((props.disable || props.readonly) && !props.forcePlaceholder ? '' : props.placeholder));
|
||||
const textBoxPlaceholder = computed(() => ((props.disable || props.readonly) && !props.forcePlaceholder ? '' : props.placeholder));
|
||||
|
||||
const isTextBoxReadonly = computed(() => props.readonly || !props.editable);
|
||||
const isTextBoxReadonly = computed(() => props.readonly || !props.editable);
|
||||
|
||||
let focusState = false;
|
||||
let focusState = false;
|
||||
|
||||
const hasFocusedTextBox = computed(() => focusState);
|
||||
const hasFocusedTextBox = computed(() => focusState);
|
||||
|
||||
const textBoxClass = computed(() => ({
|
||||
'text-left': props.textAlign === 'left',
|
||||
'text-center': props.textAlign === 'center',
|
||||
'text-right': props.textAlign === 'right',
|
||||
'form-control': true,
|
||||
'f-utils-fill': true,
|
||||
}));
|
||||
const textBoxClass = computed(() => ({
|
||||
'text-left': props.textAlign === 'left',
|
||||
'text-center': props.textAlign === 'center',
|
||||
'text-right': props.textAlign === 'right',
|
||||
'form-control': true,
|
||||
'f-utils-fill': true
|
||||
}));
|
||||
|
||||
function changeTextBoxValue(newValue: string, showEmitChangeEmit = true) {
|
||||
if (modelValue.value !== newValue) {
|
||||
modelValue.value = newValue;
|
||||
if (showEmitChangeEmit) {
|
||||
context.emit('change', newValue);
|
||||
}
|
||||
function changeTextBoxValue(newValue: string, showEmitChangeEmit = true) {
|
||||
if (modelValue.value !== newValue) {
|
||||
modelValue.value = newValue;
|
||||
if (showEmitChangeEmit) {
|
||||
context.emit('change', newValue);
|
||||
}
|
||||
context.emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value: string) => context.emit('change', value)
|
||||
);
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value: string) => context.emit('change', value)
|
||||
);
|
||||
|
||||
function onBlurTextBox($event: Event) {
|
||||
focusState = false;
|
||||
context.emit('blur', $event);
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
function onClickTextBox($event: Event) {
|
||||
context.emit('click', $event);
|
||||
}
|
||||
|
||||
function onFocusTextBox($event: Event) {
|
||||
if (props.disable) {
|
||||
return;
|
||||
function onBlurTextBox($event: Event) {
|
||||
focusState = false;
|
||||
context.emit('blur', $event);
|
||||
$event.stopPropagation();
|
||||
}
|
||||
focusState = true;
|
||||
if (!isTextBoxReadonly.value) {
|
||||
context.emit('focus', $event);
|
||||
|
||||
function onClickTextBox($event: Event) {
|
||||
context.emit('click', $event);
|
||||
}
|
||||
}
|
||||
|
||||
function onInput($event: Event) {
|
||||
context.emit('input', ($event.target as HTMLInputElement).value);
|
||||
const newValue = ($event.target as HTMLInputElement).value;
|
||||
displayText.value = newValue;
|
||||
if (modelValue.value !== newValue) {
|
||||
changeTextBoxValue(newValue, false);
|
||||
context.emit('update:modelValue', ($event.target as HTMLInputElement).value);
|
||||
function onFocusTextBox($event: Event) {
|
||||
if (props.disable) {
|
||||
return;
|
||||
}
|
||||
focusState = true;
|
||||
if (!isTextBoxReadonly.value) {
|
||||
context.emit('focus', $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseDownTextBox($event: MouseEvent) {
|
||||
const target = $event.target as HTMLElement;
|
||||
if (target.tagName !== 'INPUT') {
|
||||
$event.preventDefault();
|
||||
function onInput($event: Event) {
|
||||
context.emit('input', ($event.target as HTMLInputElement).value);
|
||||
const newValue = ($event.target as HTMLInputElement).value;
|
||||
displayText.value = newValue;
|
||||
if (modelValue.value !== newValue) {
|
||||
changeTextBoxValue(newValue, false);
|
||||
// context.emit('update:modelValue', ($event.target as HTMLInputElement).value);
|
||||
}
|
||||
}
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
function onKeyDownTextBox($event: Event) {
|
||||
context.emit('keydown', $event);
|
||||
}
|
||||
function onMouseDownTextBox($event: MouseEvent) {
|
||||
const target = $event.target as HTMLElement;
|
||||
if (target.tagName !== 'INPUT') {
|
||||
$event.preventDefault();
|
||||
}
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
function onKeyUpTextBox($event: Event) {
|
||||
context.emit('keyup', $event);
|
||||
}
|
||||
function onKeyDownTextBox($event: Event) {
|
||||
context.emit('keydown', $event);
|
||||
}
|
||||
|
||||
function onTextBoxValueChange($event: Event) {
|
||||
const newValue = ($event.target as HTMLInputElement).value;
|
||||
changeTextBoxValue(newValue);
|
||||
}
|
||||
function onKeyUpTextBox($event: Event) {
|
||||
context.emit('keyup', $event);
|
||||
}
|
||||
|
||||
return {
|
||||
hasFocusedTextBox,
|
||||
isTextBoxReadonly,
|
||||
textBoxClass,
|
||||
textBoxPlaceholder,
|
||||
textBoxTitle,
|
||||
changeTextBoxValue,
|
||||
onBlurTextBox,
|
||||
onClickTextBox,
|
||||
onFocusTextBox,
|
||||
onInput,
|
||||
onKeyDownTextBox,
|
||||
onKeyUpTextBox,
|
||||
onMouseDownTextBox,
|
||||
onTextBoxValueChange,
|
||||
};
|
||||
function onTextBoxValueChange($event: Event) {
|
||||
const newValue = ($event.target as HTMLInputElement).value;
|
||||
changeTextBoxValue(newValue);
|
||||
}
|
||||
|
||||
return {
|
||||
hasFocusedTextBox,
|
||||
isTextBoxReadonly,
|
||||
textBoxClass,
|
||||
textBoxPlaceholder,
|
||||
textBoxTitle,
|
||||
changeTextBoxValue,
|
||||
onBlurTextBox,
|
||||
onClickTextBox,
|
||||
onFocusTextBox,
|
||||
onInput,
|
||||
onKeyDownTextBox,
|
||||
onKeyUpTextBox,
|
||||
onMouseDownTextBox,
|
||||
onTextBoxValueChange
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import type { App } from 'vue';
|
||||
import FButton from './src/button.component';
|
||||
import FButtonGroup from './src/button-group.component';
|
||||
|
||||
export * from './src/button.props';
|
||||
export * from './src/button-group.props';
|
||||
|
||||
|
||||
export { FButton };
|
||||
|
||||
export default {
|
||||
install(app: App): void {
|
||||
app.component(FButton.name, FButton);
|
||||
},
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import { defineComponent, computed } from 'vue';
|
||||
import type { SetupContext } from 'vue';
|
||||
import { buttonGroupProps, ButtonGroupProps } from './button-group.props';
|
||||
import { useButtonGroup } from './composition/use-button-group';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FButtonGroup',
|
||||
props: buttonGroupProps,
|
||||
emits: ['click', 'changeState', 'change', 'clickMenuOut'],
|
||||
setup(props: ButtonGroupProps, context: SetupContext) {
|
||||
// const { onClickButton } = useButtonGroup(props, context);
|
||||
const fButtonGroupSize = computed(() => ({
|
||||
'btn-group-lg': props.size === 'large',
|
||||
'btn-group-sm': props.size === 'small'
|
||||
}));
|
||||
// 样式:
|
||||
// 'btn '+
|
||||
// (btn.type?'btn-'+ btn.type:'btn-link')+
|
||||
// ' '+
|
||||
// (btn.type && btn.type !== 'link' ? 'f-btn-ml' :'')
|
||||
|
||||
// btn btn-btn.type f-btn-ml
|
||||
// btn btn-link
|
||||
|
||||
// const fButtonType = computed(() => ({
|
||||
// 'btn-primary': props.buttonType === 'primary',
|
||||
// 'btn-warning': props.buttonType === 'warning',
|
||||
// 'btn-danger': props.buttonType === 'danger',
|
||||
// 'btn-success': props.buttonType === 'success',
|
||||
// 'btn-link': props.buttonType === 'link',
|
||||
// 'btn-secondary': props.buttonType === 'secondary',
|
||||
// }));
|
||||
const theFlatButtons = useButtonGroup(props, context);
|
||||
|
||||
return () => '';
|
||||
}
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
import { ExtractPropTypes, PropType } from 'vue';
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
type PlacementDirection = 'top' | 'top-left' | 'top-right' | 'left' | 'left-top' | 'left-bottom' | 'bottom' | 'bottom-left' | 'bottom-right' | 'right' | 'right-top' | 'right-bottom';
|
||||
|
||||
export const buttonGroupProps = {
|
||||
/**
|
||||
* 组件标识
|
||||
*/
|
||||
id: String,
|
||||
/**
|
||||
* 横向纠正的参照
|
||||
*/
|
||||
rectifyReferenceH: { type: HTMLElement, default: 'referenceEl' },
|
||||
/**
|
||||
* 纵向纠正的参照
|
||||
*/
|
||||
rectifyReferenceV: { type: HTMLElement, default: 'referenceEl' },
|
||||
/**
|
||||
* 是否自动纠正位置
|
||||
*/
|
||||
autoRectify: { type: Boolean, default: 'true' },
|
||||
/**
|
||||
* 计算方向的真正placement
|
||||
*/
|
||||
realPlacement: { type: String, default: 'bottom-right' },
|
||||
/**
|
||||
* 重新计算后的placement
|
||||
*/
|
||||
rectifyPlacement: { type: String, default: 'bottom-right' },
|
||||
/**
|
||||
* 按钮信息
|
||||
*/
|
||||
data: { type: Array },
|
||||
/**
|
||||
* 显示的按钮数量 默认为2
|
||||
*/
|
||||
count: { type: Number, default: 2 },
|
||||
/**
|
||||
* 按钮大小
|
||||
*/
|
||||
size: { type: String, default: 'small' },
|
||||
/**
|
||||
* 按钮样式
|
||||
*/
|
||||
type: { type: String, default: 'primary' },
|
||||
/**
|
||||
* 按钮展示位置
|
||||
*/
|
||||
placement: { type: String, default: 'bottom' }, /**
|
||||
/**
|
||||
* 下拉面板显示
|
||||
*/
|
||||
showPanel: { type: Boolean, default: false },
|
||||
/**
|
||||
* 下拉面板显示标志
|
||||
*/
|
||||
dpFlag: { type: Boolean, default: false },
|
||||
|
||||
};
|
||||
export default buttonGroupProps;
|
||||
|
||||
export type ButtonGroupProps = ExtractPropTypes<typeof buttonGroupProps>;
|
|
@ -0,0 +1,102 @@
|
|||
import { defineComponent, computed } from 'vue';
|
||||
import type { SetupContext } from 'vue';
|
||||
import { buttonProps, ButtonProps } from './button.props';
|
||||
import { useButton } from './composition/use-button';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FButton',
|
||||
props: buttonProps,
|
||||
emits: ['click'],
|
||||
setup(props: ButtonProps, context: SetupContext) {
|
||||
const { onClickButton } = useButton(props, context);
|
||||
const fButtonSize = computed(() => ({
|
||||
'btn-lg': props.size === 'large',
|
||||
'btn-sm': props.size === 'small',
|
||||
}));
|
||||
const fButtonType = computed(() => ({
|
||||
'btn-primary': props.buttonType === 'primary',
|
||||
'btn-warning': props.buttonType === 'warning',
|
||||
'btn-danger': props.buttonType === 'danger',
|
||||
'btn-success': props.buttonType === 'success',
|
||||
'btn-link': props.buttonType === 'link',
|
||||
'btn-secondary': props.buttonType === 'secondary',
|
||||
}));
|
||||
|
||||
return () => (
|
||||
// btn-lg btn btn-primary
|
||||
// btn-sm btn btn-warning
|
||||
<div>
|
||||
<div style={'text-align:left;margin-top:10px;'}>
|
||||
<div style={'margin-top:10px;'}>primary</div>
|
||||
<button
|
||||
class={[fButtonSize.value, ' btn btn-primary']}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
主要按钮
|
||||
</button>
|
||||
<button
|
||||
class={[fButtonSize.value, ' btn btn-danger']}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
危险按钮
|
||||
</button>
|
||||
<button
|
||||
class={[fButtonSize.value, ' btn btn-success']}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
成功按钮
|
||||
</button>
|
||||
<button
|
||||
class={[fButtonSize.value, ' btn btn-warning']}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
警告按钮
|
||||
</button>
|
||||
<button
|
||||
class={[fButtonSize.value, ' btn btn-secondary']}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
信息按钮
|
||||
</button>
|
||||
<button
|
||||
class={[fButtonSize.value, ' btn btn-link']}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
文本按钮
|
||||
</button>
|
||||
</div>
|
||||
<div style={'text-align:left'}>
|
||||
<div style={'margin-top:10px;'}>size</div>
|
||||
<button
|
||||
class={['btn-sm btn', fButtonType.value]}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
小尺寸
|
||||
</button>
|
||||
<button
|
||||
class={['btn-lg btn', fButtonType.value]}
|
||||
style={'margin:5px'}
|
||||
id={props.id}
|
||||
disabled={props.disable}
|
||||
onClick={onClickButton}>
|
||||
大尺寸
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
import { ExtractPropTypes, PropType } from 'vue';
|
||||
|
||||
type ButtonType = 'primary' | 'warning' | 'danger' | 'success' | 'link' | 'secondary';
|
||||
type SizeType = 'small' | 'large';
|
||||
|
||||
export const buttonProps = {
|
||||
/**
|
||||
* 组件标识
|
||||
*/
|
||||
id: String,
|
||||
/**
|
||||
* 设置按钮类型
|
||||
*/
|
||||
buttonType: { type: String as PropType<ButtonType>, default: 'primary' },
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disable: { type: Boolean, default: false },
|
||||
/**
|
||||
* 按钮尺寸
|
||||
*/
|
||||
size: { type: String as PropType<SizeType>, default: 'small' },
|
||||
// 待确定:text参数
|
||||
};
|
||||
|
||||
export type ButtonProps = ExtractPropTypes<typeof buttonProps>;
|
|
@ -0,0 +1,13 @@
|
|||
import { ComputedRef } from 'vue';
|
||||
|
||||
export interface UseButtonGroup {
|
||||
/**
|
||||
* 附加按钮的Class
|
||||
*/
|
||||
// buttonClass: ComputedRef<Record<string, boolean | undefined>>;
|
||||
/**
|
||||
* 点击附加按钮事件响应函数
|
||||
*/
|
||||
clickEvent: ($event: Event) => void;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { ComputedRef } from 'vue';
|
||||
|
||||
export interface UseButton {
|
||||
/**
|
||||
* 附加按钮的Class
|
||||
*/
|
||||
// buttonClass: ComputedRef<Record<string, boolean | undefined>>;
|
||||
/**
|
||||
* 点击附加按钮事件响应函数
|
||||
*/
|
||||
onClickButton: ($event: Event) => void;
|
||||
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
import { UseButtonGroup } from './types-group';
|
||||
import { ButtonGroupProps } from '../button-group.props';
|
||||
import { computed, SetupContext } from 'vue';
|
||||
|
||||
export function useButtonGroup(props: ButtonGroupProps, context: SetupContext): UseButtonGroup {
|
||||
// ngAfterViewChecked() {
|
||||
// if (this.show) {
|
||||
// this.setPosition(this.event)
|
||||
// }
|
||||
// }
|
||||
|
||||
// function onClickButton($event: Event) {
|
||||
// $event.stopPropagation();
|
||||
// // this.disabled
|
||||
// if (props.disable) {
|
||||
// context.emit('clickButton', $event);
|
||||
// }
|
||||
// }
|
||||
|
||||
function clickEvent($event: Event) {
|
||||
$event.stopPropagation();
|
||||
props.showPanel = !props.showPanel;
|
||||
// body添加面板
|
||||
if (props.showPanel) {
|
||||
this.appendBody();
|
||||
this.event = $event;
|
||||
// 绑定相应的事件
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
this.bindMenuMouseenter();
|
||||
this.bindiMenuMouseleave();
|
||||
this.mouseNotEnterLeave();
|
||||
});
|
||||
}
|
||||
// 面板显示 脏值检测
|
||||
this.changeRef.detectChanges();
|
||||
this.changeRef.markForCheck();
|
||||
context.emit('changeState', props.showPanel);
|
||||
}
|
||||
|
||||
/* 按钮触发事件 */
|
||||
function toggle($event: any, btn) {
|
||||
$event.stopPropagation();
|
||||
if (btn.disabled) return;
|
||||
props.showPanel = false;
|
||||
// 关闭下拉按钮面板 脏值检测
|
||||
this.changeRef.detectChanges();
|
||||
this.changeRef.markForCheck();
|
||||
context.emit('change', btn.id);
|
||||
context.emit('click', btn);
|
||||
}
|
||||
|
||||
/* 显示出来的按钮组 */
|
||||
function flatButtons() {
|
||||
return props.data && props.data.slice(0, this.count);
|
||||
}
|
||||
|
||||
function dpButtons() {
|
||||
return props.data && props.data.slice(this.count);
|
||||
}
|
||||
|
||||
// 下拉按钮显示到body中 可改变面板方向
|
||||
function appendBody() {
|
||||
if (this.dpMenu.nativeElement) {
|
||||
// 添加到body 便于全部显示
|
||||
document.body.appendChild(this.dpMenu.nativeElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当下拉超出边界时 转换方向,
|
||||
* 并未处理,边界不够下拉展示的情况
|
||||
* @param btnSize
|
||||
*/
|
||||
function changePlacement(btnSize: any) {
|
||||
if (!props.autoRectify) {
|
||||
return;
|
||||
}
|
||||
const referPosition = this.getReferencePosition();
|
||||
let newPlacement = props.realPlacement;
|
||||
if (newPlacement.indexOf('bottom') > -1) {
|
||||
if (this._menuHeight > referPosition.bottom - btnSize.bottom) {
|
||||
newPlacement = newPlacement.replace('bottom', 'top');
|
||||
}
|
||||
} else if (newPlacement.indexOf('top') > -1) {
|
||||
if (this._menuHeight > btnSize.top - referPosition.top) {
|
||||
newPlacement = newPlacement.replace('top', 'bottom');
|
||||
}
|
||||
}
|
||||
if (newPlacement.indexOf('left') > -1) {
|
||||
if (this._menuWidth > btnSize.left - referPosition.left) {
|
||||
newPlacement = newPlacement.replace('left', 'right');
|
||||
}
|
||||
} else if (newPlacement.indexOf('right') > -1) {
|
||||
if (this._menuWidth > referPosition.right - btnSize.right) {
|
||||
newPlacement = newPlacement.replace('right', 'left');
|
||||
}
|
||||
}
|
||||
this.rectifyPlacement = newPlacement;
|
||||
}
|
||||
/**
|
||||
* 确认参照的边界
|
||||
*/
|
||||
function getReferencePosition() {
|
||||
let rRight = document.documentElement.clientWidth;
|
||||
let rBottom = document.documentElement.clientHeight;
|
||||
let rTop = 0;
|
||||
let rLeft = 0;
|
||||
// 横向参照
|
||||
if (props.rectifyReferenceH) {
|
||||
rRight = props.rectifyReferenceH.getBoundingClientRect().right;
|
||||
rLeft = props.rectifyReferenceH.getBoundingClientRect().left;
|
||||
}
|
||||
// 纵向参照
|
||||
if (props.rectifyReferenceV) {
|
||||
rBottom = props.rectifyReferenceV.getBoundingClientRect().bottom;
|
||||
rTop = props.rectifyReferenceV.getBoundingClientRect().top;
|
||||
}
|
||||
return { top: rTop, left: rLeft, right: rRight, bottom: rBottom };
|
||||
}
|
||||
/**
|
||||
* 变化对应的class
|
||||
* @param position
|
||||
*/
|
||||
function _getClsName(position) {
|
||||
let className = '';
|
||||
switch (position) {
|
||||
case 'top-right':
|
||||
case 'top':
|
||||
// 朝上,朝上-朝右
|
||||
className = 'dropup';
|
||||
break;
|
||||
case 'top-left':
|
||||
// 朝上-朝左
|
||||
className = 'dropup-left';
|
||||
break;
|
||||
case 'left-bottom':
|
||||
case 'left':
|
||||
// 横向——朝左——朝下
|
||||
className = 'dropleft';
|
||||
break;
|
||||
case 'left-top':
|
||||
// 横向——朝左——朝上
|
||||
className = 'dropleft-up';
|
||||
break;
|
||||
case 'right-bottom':
|
||||
case 'right':
|
||||
// 横向——朝右——朝下
|
||||
className = 'dropright';
|
||||
break;
|
||||
case 'right-top':
|
||||
// 横向——朝右——朝上
|
||||
className = 'dropright-up';
|
||||
break;
|
||||
case 'bottom-left':
|
||||
// 朝下——朝左
|
||||
className = 'dropdown-left';
|
||||
break;
|
||||
case 'bottom-right':
|
||||
className = 'dropdown';
|
||||
break;
|
||||
default:
|
||||
// 朝下,朝下——朝右
|
||||
className = 'dropdown';
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
function getRealPlacement(pment) {
|
||||
let result = 'bottom-right';
|
||||
switch (pment) {
|
||||
case 'top':
|
||||
result = 'top-right';
|
||||
break;
|
||||
case 'left':
|
||||
result = 'left-bottom';
|
||||
break;
|
||||
case 'right':
|
||||
result = 'right-bottom';
|
||||
break;
|
||||
case 'bottom':
|
||||
result = 'bottom-right';
|
||||
break;
|
||||
default:
|
||||
result = pment;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 计算的位置区分忒细化
|
||||
*/
|
||||
function changePosition(btnSize: any) {
|
||||
let rplacement = '';
|
||||
if (props.autoRectify) {
|
||||
rplacement = this.rectifyPlacement;
|
||||
} else {
|
||||
rplacement = props.realPlacement;
|
||||
}
|
||||
let styleTop = 0;
|
||||
let styleLeft = 0;
|
||||
if (rplacement.indexOf('top') > -1) {
|
||||
styleTop = btnSize.top - this._menuHeight;
|
||||
} else if (rplacement.indexOf('bottom') > -1) {
|
||||
styleTop = btnSize.bottom;
|
||||
}
|
||||
if (rplacement.indexOf('right') > -1) {
|
||||
styleLeft = btnSize.right;
|
||||
} else if (rplacement.indexOf('left') > -1) {
|
||||
styleLeft = btnSize.left - this._menuWidth;
|
||||
}
|
||||
// 开头
|
||||
if (rplacement.indexOf('-top') > -1) {
|
||||
styleTop -= btnSize.height;
|
||||
} else if (rplacement.indexOf('-bottom') > -1) {
|
||||
styleTop += btnSize.height;
|
||||
}
|
||||
this.dpMenu.nativeElement.style.top = styleTop + 'px';
|
||||
this.dpMenu.nativeElement.style.left = styleLeft + 'px';
|
||||
}
|
||||
|
||||
/* 绑定下拉面板鼠标进入事件 */
|
||||
function bindMenuMouseenter() {
|
||||
this.mouseenterEvent = this.changeFlagToTrue.bind(this);
|
||||
this.dpMenu.nativeElement.addEventListener('mouseenter', this.mouseenterEvent);
|
||||
}
|
||||
|
||||
/* 绑定下拉面板鼠标离开事件 */
|
||||
function bindiMenuMouseleave() {
|
||||
this.mouseleaveEvent = this.mouseLeave.bind(this);
|
||||
this.dpMenu.nativeElement.addEventListener('mouseleave', this.mouseleaveEvent);
|
||||
}
|
||||
|
||||
/* 绑定点击面板区域之外触发的事件 */
|
||||
// bindDocClick() {
|
||||
// this.documentClickEvent = this.clickDoc.bind(this);
|
||||
// document.addEventListener('click', this.documentClickEvent);
|
||||
// }
|
||||
|
||||
/* 解绑事件 */
|
||||
function unbindMenuMouseenter() {
|
||||
if (this.mouseenterEvent) {
|
||||
this.dpMenu.nativeElement.removeEventListener('mouseenter', this.mouseenterEvent);
|
||||
}
|
||||
}
|
||||
function unbindiMenuMouseleave() {
|
||||
if (this.mouseleaveEvent) {
|
||||
this.dpMenu.nativeElement.removeEventListener('mouseleave', this.mouseleaveEvent);
|
||||
}
|
||||
}
|
||||
// unbindDocClick() {
|
||||
// if (this.documentClickEvent) {
|
||||
// document.removeEventListener('click', this.documentClickEvent);
|
||||
// }
|
||||
// }
|
||||
|
||||
/* flag true */
|
||||
function changeFlagToTrue() {
|
||||
props.dpFlag = true;
|
||||
}
|
||||
|
||||
/* flag false */
|
||||
function changeFlagToFalse() {
|
||||
props.dpFlag = false;
|
||||
}
|
||||
|
||||
/* 鼠标离开时 关闭menu */
|
||||
function mouseLeave() {
|
||||
if (props.dpFlag) {
|
||||
this.changeFlagToFalse();
|
||||
// this.unbindDocClick();
|
||||
this.unbindiMenuMouseleave();
|
||||
this.unbindMenuMouseenter();
|
||||
this.close();
|
||||
if (this.setTimeObj) {
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
clearTimeout(this.setTimeObj);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 鼠标没有进入到面板 一段时间后面板自动消失 */
|
||||
function mouseNotEnterLeave() {
|
||||
this.ngZone.runOutsideAngular(() => {
|
||||
this.setTimeObj = setTimeout(() => {
|
||||
if (!props.dpFlag) {
|
||||
this.changeFlagToFalse();
|
||||
// this.unbindDocClick();
|
||||
this.unbindiMenuMouseleave();
|
||||
this.unbindMenuMouseenter();
|
||||
this.close();
|
||||
if (this.setTimeObj) {
|
||||
clearTimeout(this.setTimeObj);
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
/* 关闭下拉面板 */
|
||||
function close() {
|
||||
props.showPanel = false;
|
||||
// 关闭下拉按钮面板 脏值检测
|
||||
if (!this.changeRef.destroyed) {
|
||||
this.changeRef.detectChanges();
|
||||
this.changeRef.markForCheck();
|
||||
}
|
||||
this.dpBtn.nativeElement.blur();
|
||||
}
|
||||
|
||||
/* 动态指定menu在body中的位置 */
|
||||
function setPosition(e) {
|
||||
// 下拉按钮
|
||||
const btnSize = this.dpBtn.nativeElement.getBoundingClientRect();
|
||||
// 下拉面板
|
||||
const menuRect = this.dpMenu.nativeElement.getBoundingClientRect();
|
||||
this._menuHeight = menuRect.height;
|
||||
this._menuWidth = menuRect.width;
|
||||
// 如果要自动纠正方向
|
||||
if (props.autoRectify) {
|
||||
this.changePlacement(btnSize);
|
||||
}
|
||||
this.changePosition(btnSize);
|
||||
}
|
||||
|
||||
return {
|
||||
clickEvent
|
||||
};
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { UseButton } from './types';
|
||||
import { ButtonProps } from '../button.props';
|
||||
import { computed, SetupContext } from 'vue';
|
||||
|
||||
export function useButton(props: ButtonProps, context: SetupContext): UseButton {
|
||||
|
||||
// const buttonClass = computed(() => ({
|
||||
// // 'input-group-append': true,
|
||||
// // 'append-force-show': props.showButtonWhenDisabled && (props.readonly || props.disable),
|
||||
// }));
|
||||
|
||||
function onClickButton($event: Event) {
|
||||
$event.stopPropagation();
|
||||
// this.disabled
|
||||
if (props.disable) {
|
||||
context.emit('clickButton', $event);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// buttonClass,
|
||||
onClickButton
|
||||
};
|
||||
}
|
|
@ -19,7 +19,7 @@ export default defineComponent({
|
|||
const toastClass = computed(() => {
|
||||
const classObject = {
|
||||
animated: showingToast.value,
|
||||
toast: true,
|
||||
toast: true
|
||||
};
|
||||
classObject[props.animate] = false;
|
||||
classObject[animateEnd] = showingToast.value;
|
||||
|
@ -93,7 +93,7 @@ export default defineComponent({
|
|||
|
||||
return () => {
|
||||
return (
|
||||
<div class={toastClass}>
|
||||
<div class={toastClass.value}>
|
||||
{shouldShowCloseButton.value && (
|
||||
<button class="toast-close f-btn-icon f-bare" onClick={onCloseToast}>
|
||||
<span class="f-icon modal_close"></span>
|
||||
|
@ -127,5 +127,5 @@ export default defineComponent({
|
|||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import { NotifyData, ToastyAnimate } from '../notify.props';
|
|||
|
||||
export const toastProps = {
|
||||
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
|
||||
options: { type: Object as PropType<NotifyData> },
|
||||
options: { type: Object as PropType<NotifyData> }
|
||||
};
|
||||
|
||||
export type ToastProps = ExtractPropTypes<typeof toastProps>;
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
import { isFunction } from 'lodash';
|
||||
import { computed, defineComponent, ref, SetupContext } from 'vue';
|
||||
import { NotifyData, NotifyProps, notifyProps } from './notify.props';
|
||||
import Toast from './components/toast.component';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Notify',
|
||||
props: notifyProps,
|
||||
emits: ['empty'],
|
||||
emits: ['close', 'empty'],
|
||||
setup(props: NotifyProps, context: SetupContext) {
|
||||
const notifyClass = computed(() => ({
|
||||
'farris-notify': true,
|
||||
'farris-notify': true
|
||||
}));
|
||||
|
||||
const defaultNotifyDistance = {
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 136,
|
||||
bottom: 12,
|
||||
bottom: 12
|
||||
};
|
||||
|
||||
const toasts = ref(props.toasts || []);
|
||||
|
@ -24,10 +25,12 @@ export default defineComponent({
|
|||
left: props.position.indexOf('left') > -1 ? `${props.left ? props.left : defaultNotifyDistance.left}px` : '',
|
||||
right: props.position.indexOf('right') > -1 ? `${props.right ? props.right : defaultNotifyDistance.right}px` : '',
|
||||
top: props.position.indexOf('top') > -1 ? `${props.top ? props.top : defaultNotifyDistance.top}px` : '',
|
||||
bottom: props.position.indexOf('bottom') > -1 ? `${props.bottom ? props.bottom : defaultNotifyDistance.bottom}px` : '',
|
||||
bottom: props.position.indexOf('bottom') > -1 ? `${props.bottom ? props.bottom : defaultNotifyDistance.bottom}px` : ''
|
||||
}));
|
||||
|
||||
function closeToast(toast: NotifyData) {}
|
||||
function closeToast(toast: NotifyData) {
|
||||
context.emit('close');
|
||||
}
|
||||
|
||||
function addToast(toast: NotifyData) {
|
||||
if (toasts.value.length >= props.limit) {
|
||||
|
@ -71,16 +74,10 @@ export default defineComponent({
|
|||
return (
|
||||
<div id={props.id} class={notifyClass.value} style={notifyStyle.value}>
|
||||
{toasts.value.map((toastData: NotifyData) => {
|
||||
return (
|
||||
<f-toast
|
||||
v-model={toastData}
|
||||
animate={props.animate}
|
||||
onClose={($event: Event) => onClose($event, toastData)}
|
||||
onClick={onClick}></f-toast>
|
||||
);
|
||||
return <Toast options={toastData} animate={props.animate} onClose={($event) => onClose($event, toastData)}></Toast>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -50,6 +50,6 @@ export const notifyProps = {
|
|||
id: { type: String },
|
||||
animate: { type: String as PropType<ToastyAnimate>, default: 'fadeIn' },
|
||||
toasts: { type: Array<NotifyData> },
|
||||
options: { type: Object as PropType<NotifyData> },
|
||||
options: { type: Object as PropType<NotifyData> }
|
||||
};
|
||||
export type NotifyProps = ExtractPropTypes<typeof notifyProps>;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { reactive, createApp, onUnmounted } from 'vue';
|
||||
import type { App } from 'vue';
|
||||
import { NotifyProps } from './notify.props';
|
||||
import Notify from './notify.component';
|
||||
|
||||
function initInstance(props: NotifyProps, content?: string): App {
|
||||
const container = document.createElement('div');
|
||||
container.style.display = 'contents';
|
||||
const app: App = createApp({
|
||||
setup() {
|
||||
onUnmounted(() => {
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
return () => <Notify {...props} onClose={app.unmount}></Notify>;
|
||||
}
|
||||
});
|
||||
document.body.appendChild(container);
|
||||
app.mount(container);
|
||||
return app;
|
||||
}
|
||||
|
||||
export default class NotifyService {
|
||||
static show(options: NotifyProps): void {
|
||||
const props: NotifyProps = reactive({
|
||||
...options
|
||||
});
|
||||
initInstance(props);
|
||||
}
|
||||
}
|
|
@ -1,26 +1,46 @@
|
|||
import { computed, defineComponent, SetupContext } from 'vue';
|
||||
import { computed, defineComponent, ref, SetupContext } from 'vue';
|
||||
import { PopoverProps, popoverProps } from './popover.props';
|
||||
|
||||
import './popover.scss';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FPopover',
|
||||
props: popoverProps,
|
||||
emits: [],
|
||||
setup(props: PopoverProps, context: SetupContext) {
|
||||
|
||||
const position = ref(props.position);
|
||||
|
||||
const shouldShowTitle = computed(() => !!props.title);
|
||||
|
||||
const popoverClass = computed(()=>{
|
||||
const originPopover = `popover in popover-${position.value}`;
|
||||
const bsPopover = `bs-popover-${position.value}`
|
||||
const popoverClassObject = {
|
||||
'popover in pop'
|
||||
};
|
||||
|
||||
'[class]':
|
||||
'"popover in popover-" + placement + " " + "bs-popover-" + placement + " " + placement + " " + containerClass',
|
||||
'[class.show]': '!isBs3',
|
||||
'[class.bs3]': 'isBs3',
|
||||
role: 'tooltip',
|
||||
style: 'display:block;'
|
||||
})
|
||||
|
||||
const popoverContainerClass = computed(() => ({
|
||||
'popover-content': true,
|
||||
'popover-body': true,
|
||||
'popover-body': true
|
||||
}));
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div class="popover-arrow arrow"></div>
|
||||
{shouldShowTitle.value && <h3 class="popover-title popover-header">{props.title}</h3>}
|
||||
<div class={popoverContainerClass.value}>{context.slots.default && context.slots?.default()}</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ export type PopoverPosition = 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
|||
|
||||
export const popoverProps = {
|
||||
title: { type: String },
|
||||
position: { type: String as PropType<PopoverPosition>, default: 'top' },
|
||||
position: { type: String as PropType<PopoverPosition>, default: 'top' }
|
||||
};
|
||||
|
||||
export type PopoverProps = ExtractPropTypes<typeof popoverProps>;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
.bs3.popover-top {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.bs3.popover.top > .arrow {
|
||||
margin-left: -2px;
|
||||
}
|
||||
.bs3.popover.top {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.popover.bottom > .arrow {
|
||||
margin-left: -4px;
|
||||
}
|
||||
.bs3.bs-popover-left {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.bs3.bs-popover-right .arrow,
|
||||
.bs3.bs-popover-left .arrow {
|
||||
margin: 0.3rem 0;
|
||||
}
|
||||
.arrow-left .arrow {
|
||||
left: calc(50% - 10px);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import type { App } from 'vue';
|
||||
import RadioGroup from './src/radio-group.component';
|
||||
|
||||
export * from './src/radio-group.props';
|
||||
|
||||
export { RadioGroup };
|
||||
|
||||
export default {
|
||||
install(app: App): void {
|
||||
app.component(RadioGroup.name, RadioGroup);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
import { ChangeRadio, Radio } from './types';
|
||||
import { computed, Ref, SetupContext } from 'vue';
|
||||
import { RadioGroupProps } from '../radio-group.props';
|
||||
|
||||
export function changeRadio(props: RadioGroupProps, context: SetupContext, modelValue: Ref<string>): ChangeRadio {
|
||||
|
||||
const canChangeRadioButton = computed(() => !props.disabled);
|
||||
const enumData = computed(() => props.enumData || []);
|
||||
|
||||
function getValue(item: Radio): any {
|
||||
return item[props.valueField];
|
||||
};
|
||||
|
||||
function getText(item: Radio): any {
|
||||
return item[props.textField];
|
||||
};
|
||||
|
||||
function onClickRadio(item, $event: Event) {
|
||||
if (canChangeRadioButton.value) {
|
||||
const newValue = getValue(item);
|
||||
if (modelValue.value !== item) {
|
||||
modelValue.value = newValue;
|
||||
|
||||
context.emit('changeValue', newValue);
|
||||
|
||||
context.emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
return {
|
||||
enumData,
|
||||
getValue,
|
||||
getText,
|
||||
onClickRadio
|
||||
};
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { ComputedRef, Ref } from 'vue';
|
||||
|
||||
export interface Radio {
|
||||
/**
|
||||
* 枚举值
|
||||
*/
|
||||
value: ComputedRef<any>;
|
||||
/**
|
||||
* 枚举展示文本
|
||||
*/
|
||||
name: ComputedRef<any>;
|
||||
}
|
||||
|
||||
export interface ChangeRadio {
|
||||
|
||||
enumData: ComputedRef<Array<Radio>>;
|
||||
|
||||
/**
|
||||
* 获取枚举值
|
||||
*/
|
||||
getValue(item: Radio): any;
|
||||
/**
|
||||
* 获取枚举文本
|
||||
*/
|
||||
getText(item: Radio): any;
|
||||
|
||||
/**
|
||||
* 切换单选按钮事件
|
||||
*/
|
||||
onClickRadio: (item: Radio, $event: Event) => void;
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { defineComponent, computed, ref } from 'vue';
|
||||
import type { SetupContext } from 'vue';
|
||||
import { radioGroupProps, RadioGroupProps } from './radio-group.props';
|
||||
import { changeRadio } from './composition/change-radio';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FRadioGroup',
|
||||
props: radioGroupProps,
|
||||
emits: [
|
||||
'changeValue',
|
||||
'update:modelValue',
|
||||
],
|
||||
setup(props: RadioGroupProps, context: SetupContext) {
|
||||
const modelValue = ref(props.modelValue);
|
||||
const { enumData, onClickRadio, getValue, getText } = changeRadio(props, context, modelValue);
|
||||
|
||||
|
||||
const horizontalClass = computed(() => ({
|
||||
'farris-checkradio-hor': props.horizontal
|
||||
}));
|
||||
|
||||
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={['farris-input-wrap', horizontalClass.value]}>
|
||||
{
|
||||
enumData.value.map((item, index) => {
|
||||
const id = 'radio_' + props.name + index;
|
||||
|
||||
return (
|
||||
<div class="custom-control custom-radio" >
|
||||
<input
|
||||
type="radio"
|
||||
class="custom-control-input"
|
||||
name={props.name}
|
||||
id={id}
|
||||
value={getValue(item)}
|
||||
checked={getValue(item) === modelValue.value}
|
||||
disabled={props.disabled}
|
||||
tabindex={props.tabIndex}
|
||||
onClick={(event: MouseEvent) => onClickRadio(item, event)}
|
||||
/>
|
||||
<label class="custom-control-label" for={id}>
|
||||
{ getText(item) }
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import { ExtractPropTypes, PropType } from 'vue';
|
||||
import { Radio } from './composition/types';
|
||||
|
||||
export const radioGroupProps = {
|
||||
/**
|
||||
* 组件标识
|
||||
*/
|
||||
id: String,
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
name: { type: String, default: '' },
|
||||
/**
|
||||
* 单选组枚举数组
|
||||
*/
|
||||
enumData: Array<Radio>,
|
||||
/**
|
||||
* 枚举数组中展示文本的key值。
|
||||
*/
|
||||
textField: { type: String, default: 'name' },
|
||||
/**
|
||||
* 枚举数组中枚举值的key值。
|
||||
*/
|
||||
valueField: { type: String, default: 'value' },
|
||||
/**
|
||||
* 组件是否水平排列
|
||||
*/
|
||||
horizontal: { type: Boolean, default: false },
|
||||
/**
|
||||
* 禁用组件,不允许切换单选值
|
||||
*/
|
||||
disabled: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 组件值
|
||||
*/
|
||||
modelValue: { type: String, default: '' },
|
||||
|
||||
/**
|
||||
* 输入框Tab键索引
|
||||
*/
|
||||
tabIndex: Number,
|
||||
};
|
||||
|
||||
export type RadioGroupProps = ExtractPropTypes<typeof radioGroupProps>;
|
|
@ -10,6 +10,14 @@ export default defineComponent({
|
|||
return true;
|
||||
});
|
||||
|
||||
const shouldShowHeaderTitle = computed(() => {
|
||||
return true;
|
||||
});
|
||||
|
||||
const shouldShowSubHeaderTitle = computed(() => {
|
||||
return true;
|
||||
});
|
||||
|
||||
const shouldShowToolbarInHeader = computed(() => {
|
||||
return false;
|
||||
});
|
||||
|
@ -28,65 +36,68 @@ export default defineComponent({
|
|||
|
||||
const toolbarButtons = ref([]);
|
||||
|
||||
const headerClass = computed(() => ({}));
|
||||
const headerClass = computed(() => {
|
||||
const customClassArray = props.headerClass.split(' ');
|
||||
const headClassObject = {
|
||||
'f-section-header': true
|
||||
};
|
||||
customClassArray.reduce<Record<string, unknown>>((classObject, classString) => {
|
||||
classObject[classString] = true;
|
||||
return classObject;
|
||||
}, headClassObject);
|
||||
return headClassObject;
|
||||
});
|
||||
|
||||
const extendAreaClass = computed(() => ({}));
|
||||
|
||||
const contentClass = computed(() => ({
|
||||
'f-section-content': true,
|
||||
const extendAreaClass = computed(() => ({
|
||||
'f-section-extend': true
|
||||
}));
|
||||
|
||||
function getToolbarState() {
|
||||
const contentClass = computed(() => {
|
||||
const customClassArray = props.contentClass.split(' ');
|
||||
const contentClassObject = {
|
||||
'f-section-content': true
|
||||
};
|
||||
customClassArray.reduce<any>((classObject, classString) => {
|
||||
classObject[classString] = true;
|
||||
return classObject;
|
||||
}, contentClassObject);
|
||||
return contentClassObject;
|
||||
});
|
||||
|
||||
function getToolbarState(buttonId: string, visiableMap: any, defaultValue: boolean) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const mainTitle = ref(props.mainTitle);
|
||||
|
||||
const subTitle = ref(props.subTitle);
|
||||
|
||||
function renderSectionHeader() {
|
||||
return (
|
||||
shouldShowHeader.value && (
|
||||
<div class={headerClass.value}>
|
||||
{shouldShowHeaderTitle.value && (
|
||||
<div class="f-title">
|
||||
<h4 class="f-title-text">{mainTitle.value}</h4>
|
||||
{shouldShowSubHeaderTitle.value && <span>{subTitle}</span>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function renderSectionContent() {
|
||||
return <div class={contentClass.value}>{context.slots.default && context.slots.default()}</div>;
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
{shouldShowHeader.value && (
|
||||
<div class="f-section-header" class={headerClass.value}>
|
||||
{shouldShowToolbarInHeader.value && (
|
||||
<div class="f-section-header--btn-placeholder">
|
||||
{toolbarButtons.value
|
||||
.filter((toolButton: ButtonConfig) => getToolbarState(toolButton.id, btnVisible, true))
|
||||
.forEach((toolButton: ButtonConfig) => {
|
||||
return <button class={toolButton.appearance.class}>{toolButton.title}</button>;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{shouldShowToolbarInContent.value && (
|
||||
<div class="f-section-header--btn-placeholder">
|
||||
{toolbarButtons.value
|
||||
.filter((toolButton: ButtonConfig) => getToolbarState(toolButton.id, btnVisible, true))
|
||||
.forEach((toolButton: ButtonConfig) => {
|
||||
return <button class={toolButton.appearance.class}>{toolButton.title}</button>;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{shouldShowExtendArea.value && (
|
||||
<div class="f-section-extend" class={extendAreaClass.value}>
|
||||
{context.slots.extend && context.slots.extend()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class={contentClass.value}>
|
||||
{shouldShowToolbarTemplateInContent.value && (
|
||||
<div class="f-section-toolbar f-section-content--toolbar">
|
||||
{context.slots.toolbarButtons &&
|
||||
context.slots.toolbarButtons({
|
||||
datas: toolbarBtns,
|
||||
dpHidden: toolbarDpHidden,
|
||||
dpDatas: inMoreButtonContents,
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{context.slots.default && context.slots.default()}
|
||||
</div>
|
||||
</>
|
||||
<div class="f-section">
|
||||
{renderSectionHeader()}
|
||||
{renderSectionContent()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { ExtractPropTypes, PropType } from 'vue';
|
||||
|
||||
export interface ButtonAppearance {
|
||||
class: string;
|
||||
}
|
||||
|
||||
export interface ButtonConfig {
|
||||
id: string;
|
||||
disable: boolean;
|
||||
title: string;
|
||||
click: any;
|
||||
appearance: object;
|
||||
appearance: ButtonAppearance;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
|
@ -32,5 +36,6 @@ export const sectionProps = {
|
|||
toolbar: { type: Object as PropType<ToolbarConfig>, default: {} },
|
||||
showToolbarMoreButton: { type: Boolean, default: true },
|
||||
clickThrottleTime: { type: Number, default: 350 },
|
||||
headerClass: { type: String, default: '' }
|
||||
};
|
||||
export type SectionProps = ExtractPropTypes<typeof sectionProps>;
|
||||
|
|
|
@ -1,24 +1,15 @@
|
|||
import { computed, defineComponent, ref, SetupContext } from 'vue';
|
||||
import { switchProps, SwitchProps, SwitchType } from './switch.props';
|
||||
import { computed, defineComponent, onMounted, ref, SetupContext, toRefs, watch } from 'vue';
|
||||
import { switchProps, SwitchProps } from './switch.props';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FSwitch',
|
||||
props: switchProps,
|
||||
emits: [],
|
||||
emits: ['update:modelValue'],
|
||||
setup(props: SwitchProps, context: SetupContext) {
|
||||
const checked = ref(false);
|
||||
const { disable, editable, square, size, checkedLabel, uncheckedLabel } = toRefs(props);
|
||||
|
||||
const disable = ref(false);
|
||||
|
||||
const editable = ref(false);
|
||||
|
||||
const squire = ref(false);
|
||||
|
||||
const size = ref(props.size);
|
||||
|
||||
const checkedLabel = ref('');
|
||||
|
||||
const uncheckedLabel = ref('');
|
||||
const checked = ref(props.checked);
|
||||
const modelValue = ref(props.modelValue);
|
||||
|
||||
function getColor(flag = '') {
|
||||
if (flag === 'borderColor') {
|
||||
|
@ -53,35 +44,64 @@ export default defineComponent({
|
|||
'f-cmp-switch': true,
|
||||
checked: checked.value,
|
||||
disabled: disable.value || !editable.value,
|
||||
squire: squire.value,
|
||||
squire: square.value,
|
||||
'switch-large': size.value === 'large',
|
||||
'switch-medium': size.value === 'medium',
|
||||
'switch-small': size.value === 'small',
|
||||
'switch-small': size.value === 'small'
|
||||
}));
|
||||
|
||||
const switchContainerStyle = computed(() => ({
|
||||
outline: 'none',
|
||||
'backgroud-color': getBackgroundColor(),
|
||||
'border-color': getBorderColor(),
|
||||
'border-color': getBorderColor()
|
||||
}));
|
||||
|
||||
const smallStyle = computed(() => ({
|
||||
background: getSwitchColor(),
|
||||
background: getSwitchColor()
|
||||
}));
|
||||
|
||||
const shouldShowSwitch = computed(() => {
|
||||
// checkedLabel || uncheckedLabel
|
||||
return checkedLabel.value || uncheckedLabel.value;
|
||||
return checkedLabel?.value || uncheckedLabel?.value;
|
||||
});
|
||||
|
||||
function updateChecked($event: any, emitClick = true) {
|
||||
if (disable.value || !editable.value) {
|
||||
return;
|
||||
}
|
||||
checked.value = !checked.value;
|
||||
modelValue.value = checked.value;
|
||||
context.emit('update:modelValue', checked.value);
|
||||
}
|
||||
|
||||
function onToggle($event: MouseEvent) {
|
||||
updateChecked($event);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value: boolean) => {
|
||||
checked.value = value;
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
checked.value = props.modelValue;
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
<span tabindex="0" role="button" class={switchContainerClass.value} style={switchContainerStyle.value}>
|
||||
<span
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class={switchContainerClass.value}
|
||||
style={switchContainerStyle.value}
|
||||
onClick={onToggle}>
|
||||
{shouldShowSwitch.value && (
|
||||
<span class="switch-pane">
|
||||
<span class="switch-label-checked">{checkedLabel.value}</span>
|
||||
<span class="switch-label-unchecked">{uncheckedLabel.value}</span>
|
||||
<span class="switch-label-checked">{checkedLabel?.value}</span>
|
||||
<span class="switch-label-unchecked">{uncheckedLabel?.value}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
|
@ -90,5 +110,5 @@ export default defineComponent({
|
|||
</>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,12 +11,16 @@ export const switchProps = {
|
|||
defaultBorderColor: { type: String },
|
||||
checkedLabel: { type: String },
|
||||
uncheckedLabel: { type: String },
|
||||
checked: { type: Boolean },
|
||||
checked: { type: Boolean, default: false },
|
||||
readonly: { type: Boolean },
|
||||
disable: { type: Boolean },
|
||||
editable: { type: Boolean, default: true },
|
||||
reverse: { type: Boolean },
|
||||
trueValue: { type: Object, default: true },
|
||||
falseValue: { type: Object, default: false },
|
||||
/**
|
||||
* 组件值
|
||||
*/
|
||||
modelValue: { type: Boolean, default: false }
|
||||
};
|
||||
export type SwitchProps = ExtractPropTypes<typeof switchProps>;
|
||||
|
|
|
@ -30,7 +30,7 @@ export default defineComponent({
|
|||
fill.value || tabType.value === 'fill';
|
||||
});
|
||||
|
||||
const shouldShowNavPills = computed(() => {});
|
||||
const shouldShowNavPills = computed(() => { });
|
||||
|
||||
const { setActiveId } = useTabs(props, context)
|
||||
|
||||
|
@ -39,11 +39,11 @@ export default defineComponent({
|
|||
'farris-tabs-inHead': hasInHeadClass.value,
|
||||
'farris-tabs-inContent': !hasInHeadClass.value,
|
||||
'farris-tabs-nav-fill': shouldShowNavFill.value,
|
||||
'farris-tabs-nav-pills': shouldShowNavPills.value,
|
||||
'farris-tabs-nav-pills': shouldShowNavPills.value
|
||||
}));
|
||||
|
||||
const tabsTitleStyle = computed(() => ({
|
||||
width: hasInHeadClass.value ? (props.titleWidth ? `${props.titleWidth}%` : '') : '',
|
||||
width: hasInHeadClass.value ? (props.titleWidth ? `${props.titleWidth}%` : '') : ''
|
||||
}));
|
||||
|
||||
const tabsTitleButtonClass = computed(() => ({
|
||||
|
@ -51,14 +51,14 @@ export default defineComponent({
|
|||
'sc-nav-btn': true,
|
||||
'px-1': true,
|
||||
'sc-nav-lr': true,
|
||||
'd-none': hideButtons.value,
|
||||
'd-none': hideButtons.value
|
||||
}));
|
||||
|
||||
const tabParentClass = computed(() => ({
|
||||
spacer: true,
|
||||
'f-utils-fill': true,
|
||||
'spacer-sides': !hideButtons.value && hideDropDown.value,
|
||||
'spacer-sides-dropdown': !hideButtons.value && !hideDropDown.value,
|
||||
'spacer-sides-dropdown': !hideButtons.value && !hideDropDown.value
|
||||
}));
|
||||
|
||||
const tabContainerClass = computed(() => ({
|
||||
|
@ -68,9 +68,9 @@ export default defineComponent({
|
|||
'nav-fill': fill.value || tabType.value === 'fill',
|
||||
'nav-pills': tabType.value === 'pills',
|
||||
'flex-row': position.value === 'top' || position.value === 'bottom',
|
||||
'flex-column': position.value === 'left' || position.value === 'right',
|
||||
'flex-column': position.value === 'left' || position.value === 'right'
|
||||
}));
|
||||
|
||||
|
||||
let tabPages = context.slots.default && context.slots.default()
|
||||
|
||||
tabPages?.forEach((tabPage: any) => {
|
||||
|
@ -126,7 +126,7 @@ export default defineComponent({
|
|||
return {
|
||||
'st-tab-text': true,
|
||||
'farris-title-auto': props.autoTitleWidth,
|
||||
'farris-title-text-custom': tab.titleOverflow,
|
||||
'farris-title-text-custom': tab.titleOverflow
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -179,5 +179,5 @@ export default defineComponent({
|
|||
</>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,14 +15,14 @@ export default defineComponent({
|
|||
const textClass = computed(() => ({
|
||||
'f-form-control-text': !isTextArea.value,
|
||||
'f-form-context-textarea': isTextArea,
|
||||
'f-component-text-auto-size': autoSize.value,
|
||||
'f-component-text-auto-size': autoSize.value
|
||||
}));
|
||||
|
||||
const textStyle = computed(() => ({
|
||||
textalign: textAlginment.value,
|
||||
height: !autoSize.value && height.value > 0 ? `${height.value}px` : '',
|
||||
'min-height': !autoSize.value && height.value > 0 ? `${height.value}px` : '',
|
||||
'max-height': !autoSize.value && maxHeight.value > 0 ? `${maxHeight.value}px` : '',
|
||||
'max-height': !autoSize.value && maxHeight.value > 0 ? `${maxHeight.value}px` : ''
|
||||
}));
|
||||
|
||||
const text = computed(() => {
|
||||
|
@ -37,5 +37,5 @@ export default defineComponent({
|
|||
</span>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ export default defineComponent({
|
|||
|
||||
const tooltipClass = computed(() => ({
|
||||
tooltip: true,
|
||||
show: true,
|
||||
show: true
|
||||
}));
|
||||
|
||||
const shouldShowTooltipText = computed(() => isTextContext.value);
|
||||
|
@ -31,5 +31,5 @@ export default defineComponent({
|
|||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -18,6 +18,6 @@ export const tooltipProps = {
|
|||
content: { type: String },
|
||||
width: { type: Number },
|
||||
customClass: { type: String },
|
||||
position: { type: String as PropType<TooltipPosition>, default: 'top' },
|
||||
position: { type: String as PropType<TooltipPosition>, default: 'top' }
|
||||
};
|
||||
export type TooltipProps = ExtractPropTypes<typeof tooltipProps>;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,48 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
// This starter template is using Vue 3 <script setup> SFCs
|
||||
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
|
||||
import { ref } from "vue";
|
||||
import { ref } from 'vue';
|
||||
import HelloWorld from './components/hello-world.vue';
|
||||
import ButtonEdit from "../components/button-edit/src/button-edit.component";
|
||||
import Avatar from './components/avatar.vue';
|
||||
import Tabs from './components/tabs.vue'
|
||||
import ButtonEdit from './components/button-edit.vue';
|
||||
import FButton from '../components/button/src/button.component';
|
||||
import Switch from './components/switch.vue';
|
||||
import RadioGroup from './components/radio-group.vue';
|
||||
import Section from './components/section.vue';
|
||||
import Notify from './components/notify.vue';
|
||||
import Accordion from './components/accordion.vue';
|
||||
|
||||
const canEdit = ref(true);
|
||||
const disable = ref(false);
|
||||
const canAutoComplete = ref(false);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a href="https://vitejs.dev" target="_blank">
|
||||
<img src="/vite.svg" class="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://vuejs.org/" target="_blank">
|
||||
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
|
||||
</a>
|
||||
</div>
|
||||
<input type="checkbox" id="checkbox" v-model="canEdit" />
|
||||
<div>
|
||||
<a href="https://vitejs.dev" target="_blank">
|
||||
<img src="/vite.svg" class="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://vuejs.org/" target="_blank">
|
||||
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
|
||||
</a>
|
||||
</div>
|
||||
<HelloWorld msg="Vite + Vue" />
|
||||
<!-- <input type="checkbox" id="checkbox" v-model="canEdit" />
|
||||
<label for="checkbox">editable:{{ canEdit }}</label>
|
||||
<input type="checkbox" id="checkbox" v-model="canAutoComplete" />
|
||||
<label for="checkbox">auto complete:{{ canAutoComplete }}</label>
|
||||
<HelloWorld msg="Vite + Vue" />
|
||||
<ButtonEdit :editable="canEdit" :auto-complete="canAutoComplete" :enable-clear="true"></ButtonEdit>
|
||||
<Avatar></Avatar>
|
||||
<Tabs />
|
||||
<label for="checkbox">auto complete:{{ canAutoComplete }}</label> -->
|
||||
<!-- <ButtonEdit :editable="canEdit" :auto-complete="canAutoComplete" :enable-clear="true"></ButtonEdit> -->
|
||||
<ButtonEdit></ButtonEdit>
|
||||
<Avatar></Avatar>
|
||||
<input type="checkbox" id="checkbox" v-model="disable" />
|
||||
<label for="checkbox">disable:{{ disable }}</label>
|
||||
<FButton :disable="disable"></FButton>
|
||||
<Switch></Switch>
|
||||
<RadioGroup></RadioGroup>
|
||||
<Section></Section>
|
||||
<Notify></Notify>
|
||||
<Accordion></Accordion>
|
||||
<Tabs />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
|
||||
.logo.vue:hover {
|
||||
filter: drop-shadow(0 0 2em #42b883aa);
|
||||
filter: drop-shadow(0 0 2em #42b883aa);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import According from '../../components/according/src/according.component';
|
||||
import AccordingItem from '../../components/according/src/components/according-item.component';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<According>
|
||||
<AccordingItem title="According Panel One"></AccordingItem>
|
||||
<AccordingItem title="According Panel Two"></AccordingItem>
|
||||
</According>
|
||||
</template>
|
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import Accordion from '../../components/accordion/src/accordion.component';
|
||||
import AccordionItem from '../../components/accordion/src/components/accordion-item.component';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Accordion>
|
||||
<AccordionItem title="According Panel One"></AccordionItem>
|
||||
<AccordionItem title="According Panel Two"></AccordionItem>
|
||||
</Accordion>
|
||||
</template>
|
|
@ -0,0 +1,18 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import ButtonEdit from '../../components/button-edit/src/button-edit.component';
|
||||
|
||||
const canEdit = ref(true);
|
||||
const canAutoComplete = ref(false);
|
||||
const text = ref('');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input type="checkbox" id="checkbox" v-model="canEdit" />
|
||||
<label for="checkbox">editable:{{ canEdit }}</label>
|
||||
<input type="checkbox" id="checkbox" v-model="canAutoComplete" />
|
||||
<label for="checkbox">auto complete:{{ canAutoComplete }}</label>
|
||||
<ButtonEdit :editable="canEdit" :auto-complete="canAutoComplete" :enable-clear="true" v-model="text"></ButtonEdit>
|
||||
Text: {{text}}
|
||||
</template>
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import NotifyService from '../../components/notify/src/notify.service';
|
||||
|
||||
function showMessage() {
|
||||
NotifyService.show({
|
||||
limit: 5,
|
||||
showCloseButton: false,
|
||||
position: 'top-center',
|
||||
timeout: 3000,
|
||||
theme: 'bootstrap',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
id: '1',
|
||||
animate: 'fadeIn',
|
||||
options: { type: 'string', title: 'string', msg: 'string' },
|
||||
toasts: [{ type: 'string', title: 'string', msg: 'string' }]
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-primary" @click="showMessage">show notify</button>
|
||||
</template>
|
|
@ -0,0 +1,39 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
import RadioGroup from "../../components/radio-group/src/radio-group.component";
|
||||
|
||||
const radioDisabled = ref(false);
|
||||
const radioEnumData = ref([
|
||||
{ value: "aa", name: "标签一" },
|
||||
{ value: "bb", name: "标签二" },
|
||||
{ value: "cc", name: "标签三" },
|
||||
]);
|
||||
const radioHorizontal = ref(false);
|
||||
|
||||
const value = ref("aa");
|
||||
|
||||
function testConsole(value) {
|
||||
// console.log("change radio value to", value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p></p>
|
||||
<label for="radio_disabled" class="mr-1">是否禁用单选组</label>
|
||||
<input type="checkbox" id="radio_disabled" v-model="radioDisabled" />
|
||||
|
||||
<label for="radio_horizontaol" class="mr-1 ml-3">是否水平排列</label>
|
||||
<input type="checkbox" id="radio_horizontaol" v-model="radioHorizontal" />
|
||||
<br />
|
||||
<RadioGroup
|
||||
:disabled="radioDisabled"
|
||||
:enum-data="radioEnumData"
|
||||
:horizontal="radioHorizontal"
|
||||
v-model="value"
|
||||
@change-value="testConsole"
|
||||
>
|
||||
</RadioGroup>
|
||||
<br />
|
||||
当前选中值: {{ value }}
|
||||
</template>
|
|
@ -0,0 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
import Section from '../../components/section/src/section.component';
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Section show-header main-title="This is a section title">
|
||||
<div>
|
||||
<span>This is setion content.</span>
|
||||
</div>
|
||||
</Section>
|
||||
</template>
|
|
@ -0,0 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import Switch from '../../components/switch/src/switch.component';
|
||||
|
||||
const canEdit = ref(false);
|
||||
const canAutoComplete = ref(false);
|
||||
const text = ref('');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input type="checkbox" id="checkbox" v-model="canEdit" />
|
||||
<label for="checkbox">switch value : {{ canEdit }}</label>
|
||||
<Switch v-model="canEdit"></Switch>
|
||||
</template>
|
Loading…
Reference in New Issue