first commit

This commit is contained in:
lingxiao865
2026-02-10 08:05:03 +08:00
commit c5af079d8c
1094 changed files with 97530 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
export function getStyles(top, zIndex) {
const topStyle = top ? `top:${top}px;` : '';
const zIndexStyle = zIndex ? `z-index:${zIndex};` : '';
return topStyle + zIndexStyle;
}

View File

@@ -0,0 +1,97 @@
/** dropdown-item */
.t-dropdown-item {
position: fixed;
right: 0;
left: 0;
top: 0;
overflow: hidden;
bottom: 0;
}
.t-dropdown-item__content {
display: flex;
flex-direction: column;
z-index: 11600;
overflow: hidden;
}
.t-dropdown-item__popup-host {
display: block;
width: 100%;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
}
.t-dropdown-item__body {
flex: 1;
background: var(--td-dropdown-menu-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff)));
overflow: auto;
max-height: var(--td-dropdown-body-max-height, 560rpx);
}
.t-dropdown-item__body--tree {
display: flex;
overflow: hidden;
}
.t-dropdown-item__body--multi {
padding-top: var(--td-spacer, 16rpx);
padding-bottom: var(--td-spacer, 16rpx);
overflow-y: auto;
}
.t-dropdown-item__scroll {
max-height: var(--td-dropdown-body-max-height, 560rpx);
}
.t-dropdown-item__footer {
display: flex;
background: var(--td-dropdown-menu-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff)));
padding: 32rpx;
position: relative;
}
.t-dropdown-item__footer::after {
content: '';
display: block;
position: absolute;
top: 0;
bottom: unset;
left: unset;
right: unset;
background-color: var(--td-component-border, var(--td-gray-color-4, #dcdcdc));
}
.t-dropdown-item__footer::after {
height: 1px;
left: 0;
right: 0;
transform: scaleY(0.5);
}
:deep(.t-dropdown-item__footer-btn) {
flex: 1;
}
:deep(.t-dropdown-item__footer-btn + .t-dropdown-item__footer-btn) {
margin-left: 32rpx;
}
.t-dropdown-item__body:empty,
.t-dropdown-item__footer:empty {
display: none;
}
.t-dropdown-item__radio,
.t-dropdown-item__checkbox {
width: 100%;
overflow: scroll;
box-sizing: border-box;
}
.t-dropdown-item__radio-group,
.t-dropdown-item__checkbox-group {
display: grid;
grid-gap: 24rpx;
}
.t-dropdown-item__radio-group {
display: grid;
grid-gap: 0rpx;
}
.t-dropdown-item__checkbox-group {
padding: 32rpx;
}
.t-dropdown-item__mask {
position: fixed;
width: 100vh;
top: 0;
left: 0;
}

View File

@@ -0,0 +1,403 @@
<template>
<view
v-if="wrapperVisible"
:class="classPrefix + ' ' + tClass"
:style="tools._style([getStyles(top, zIndex), customStyle])"
>
<view
v-if="show"
:class="classPrefix + '__mask'"
:style="tools._style(['height:' + maskHeight + 'px', customStyle])"
@click="handleMaskClick"
@touchmove.stop.prevent="closeDropdown"
/>
<t-popup
:visible="show"
:z-index="zIndex + 1"
:duration="duration"
:show-overlay="showOverlay"
custom-style="position: absolute"
:overlay-props="{ style: 'position: absolute' }"
:t-class="classPrefix + '__popup-host'"
:t-class-content="classPrefix + '__content ' + tClassContent"
@leaved="onLeaved"
@visible-change="handleMaskClick"
>
<view :class="classPrefix + '__body'">
<scroll-view
v-if="!multiple && options && options.length > 0"
:class="classPrefix + '__scroll'"
scroll-y
:scroll-into-view="'id_' + dataValue"
>
<t-radio-group
:t-class="classPrefix + '__radio ' + classPrefix + '__radio-group ' + tClassColumn"
:custom-style="radioGroupCustomStyle"
:value="dataValue"
@change="handleRadioChange"
>
<view
v-for="(item, index) in options"
:id="'id_' + item[valueAlias]"
:key="index"
>
<t-radio
:placement="placement"
tabindex="0"
:checked="dataValue === item[valueAlias]"
icon="line"
:t-class="classPrefix + '__radio-item radio ' + tClassColumnItem"
:t-class-label="tClassColumnItemLabel"
:value="item[valueAlias]"
:label="item[labelAlias]"
:disabled="item.disabled"
@change="e => onRadioChange(e, item[valueAlias])"
/>
</view>
</t-radio-group>
</scroll-view>
<scroll-view
v-if="multiple && options && options.length > 0"
:class="classPrefix + '__scroll'"
scroll-y
:scroll-into-view="'id_' + firstCheckedValue"
>
<t-checkbox-group
ref="checkboxGroupRef"
:t-class="classPrefix + '__checkbox ' + classPrefix + '__checkbox-group ' + tClassColumn"
:custom-style="checkboxGroupCustomStyle"
:value="dataValue ? dataValue : []"
@change="handleRadioChange"
>
<view
v-for="(item, index) in options"
:id="'id_' + item[valueAlias]"
:key="index"
>
<t-checkbox
tabindex="0"
:t-class="classPrefix + '__checkbox-item ' + tClassColumnItem"
theme="tag"
:value="item[valueAlias]"
:label="item[labelAlias]"
:disabled="item.disabled"
:checked="dataValue.indexOf(item[valueAlias]) > -1"
@change="e => onCheckboxChange(e, item[valueAlias])"
/>
</view>
</t-checkbox-group>
</scroll-view>
<slot />
</view>
<view :class="classPrefix + '__footer ' + tClassFooter">
<slot name="footer" />
<block v-if="multiple">
<t-button
block
:t-class="footerBtnTClass"
:class="footerBtnClass"
theme="light"
content="重置"
:disabled="dataValue.length == 0"
@click="handleReset"
/>
<t-button
block
:t-class="confirmBtnTClass"
:class="confirmBtnClass"
theme="primary"
content="确定"
:disabled="dataValue.length == 0"
@click="handleConfirm"
/>
</block>
</view>
</t-popup>
</view>
</template>
<script>
import TButton from '../button/button';
import TRadio from '../radio/radio';
import TRadioGroup from '../radio-group/radio-group';
import TCheckbox from '../checkbox/checkbox';
import TCheckboxGroup from '../checkbox-group/checkbox-group';
import TPopup from '../popup/popup';
import { uniComponent } from '../common/src/index';
import { prefix } from '../common/config';
import { coalesce, getRect, getWindowInfo } from '../common/utils';
import props from './props';
import menuProps from '../dropdown-menu/props';
import tools from '../common/utils.wxs';
import { getStyles } from './computed';
import { ChildrenMixin, RELATION_MAP } from '../common/relation';
import { canUseVirtualHost } from '../common/version';
const name = `${prefix}-dropdown-item`;
export default uniComponent({
name,
options: {
styleIsolation: 'shared',
},
controlledProps: [{
key: 'value',
event: 'change',
}],
externalClasses: [
`${prefix}-class`,
`${prefix}-class-content`,
`${prefix}-class-column`,
`${prefix}-class-column-item`,
`${prefix}-class-column-item-label`,
`${prefix}-class-footer`,
],
mixins: [ChildrenMixin(RELATION_MAP.DropdownItem)],
components: {
TButton,
TRadio,
TRadioGroup,
TCheckbox,
TCheckboxGroup,
TPopup,
},
props: {
...props,
},
emits: ['close', 'closed', 'change', 'reset', 'confirm', 'opened', 'open'],
data() {
return {
prefix,
classPrefix: name,
show: false,
top: 0,
maskHeight: 0,
initValue: null,
hasChanged: false,
duration: menuProps.duration.default,
zIndex: menuProps.zIndex.default,
overlay: menuProps.showOverlay.default,
labelAlias: 'label',
valueAlias: 'value',
computedLabel: '',
firstCheckedValue: '',
dataValue: coalesce(this.value, this.defaultValue),
wrapperVisible: false,
tools,
windowTop: 0,
};
},
computed: {
footerBtnTClass() {
return canUseVirtualHost() ? this.footerBtnRealClass : '';
},
footerBtnClass() {
return !canUseVirtualHost() ? this.footerBtnRealClass : '';
},
footerBtnRealClass() {
const { classPrefix } = this;
return `${classPrefix}__footer-btn ${classPrefix}__reset-btn`;
},
confirmBtnTClass() {
return canUseVirtualHost() ? this.confirmBtnRealClass : '';
},
confirmBtnClass() {
return !canUseVirtualHost() ? this.confirmBtnRealClass : '';
},
confirmBtnRealClass() {
const { classPrefix } = this;
return `${classPrefix}__footer-btn ${classPrefix}__confirm-btn`;
},
radioGroupCustomStyle() {
return tools._style([
{
width: '100%',
overflow: 'scroll',
boxSizing: 'border-box',
display: 'grid',
gridGap: '0px',
gridTemplateColumns: `repeat(${this.optionsColumns}, 1fr)`,
},
]);
},
checkboxGroupCustomStyle() {
return tools._style([
{
width: '100%',
overflow: 'scroll',
boxSizing: 'border-box',
display: 'grid',
gridGap: '12px',
gridTemplateColumns: `repeat(${this.optionsColumns}, 1fr)`,
padding: '16px',
},
]);
},
},
watch: {
keys: {
handler(obj) {
this.labelAlias = obj?.label || 'label';
this.valueAlias = obj?.value || 'value';
},
immediate: true,
},
value: {
handler(value) {
this.dataValue = value;
},
immediate: true,
},
dataValue: {
handler(v) {
const { options, labelAlias, valueAlias } = this;
if (this.multiple) {
if (v && !Array.isArray(v)) throw TypeError('应传入数组类型的 value');
}
const target = options.find(item => item[valueAlias] === v);
if (target) {
this.computedLabel = target[labelAlias];
}
},
immediate: true,
},
label: 'getParentAllItems',
computedLabel: 'getParentAllItems',
disabled: 'getParentAllItems',
show: {
handler(visible) {
if (visible) {
this.getParentBottom(() => {
this.wrapperVisible = true;
});
}
},
immediate: true,
},
},
mounted() {
const { windowTop } = getWindowInfo();
this.windowTop = windowTop || 0;
},
methods: {
getStyles,
innerAfterLinked(target) {
const { zIndex, duration, showOverlay } = target;
this.zIndex = zIndex;
this.duration = duration;
this.showOverlay = showOverlay;
},
getParentAllItems() {
this[RELATION_MAP.DropdownItem]?.getAllItems();
},
closeDropdown() {
this[RELATION_MAP.DropdownItem].activeIdx = -1;
this.show = false;
this.$emit('close');
},
getParentBottom(cb) {
getRect(this[RELATION_MAP.DropdownItem], `#${prefix}-bar`).then((rect) => {
this.top = rect.bottom + (this.windowTop || 0);
this.maskHeight = rect.top;
setTimeout(() => {
cb();
}, 20);
});
},
handleTreeClick(e) {
const { level, value: itemValue } = e.currentTarget.dataset;
const { dataValue } = this;
dataValue[level] = itemValue;
this._trigger('change', { value: dataValue });
},
onRadioChange(e, curValue) {
this.handleRadioChange({ value: curValue });
},
onCheckboxChange(e) {
const { checkAll, dataIndeterminate } = this.$refs.checkboxGroupRef;
const { context: { value, label }, checked } = e;
this.$refs.checkboxGroupRef.updateValue({
value,
checkAll,
indeterminate: dataIndeterminate,
checked,
item: { label, value, checked },
validChildren: false,
});
},
handleRadioChange(e) {
const { value } = e;
this._trigger('change', { value });
if (!this.multiple) {
this.closeDropdown();
} else {
const firstChecked = this.options.find(item => value.includes(item.value));
if (firstChecked) {
this.firstCheckedValue = firstChecked.value;
}
}
},
handleMaskClick() {
if (this[RELATION_MAP.DropdownItem]?.closeOnClickOverlay) {
this.closeDropdown();
}
},
handleReset() {
this._trigger('change', { value: [] });
this._trigger('reset');
},
handleConfirm() {
this._trigger('confirm', { value: this.dataValue });
this.closeDropdown();
// 在关闭 popup 后才自动滚动到首个选项
// this.firstCheckedValue = this.firstCheckedValue;
},
onLeaved() {
this.wrapperVisible = false;
},
},
});
</script>
<style scoped>
@import './dropdown-item.css';
</style>
<style scoped>
.t-dropdown-item__scroll {
width: 100%;
}
:deep(.t-dropdown-item__footer-btn) {
flex: 1;
}
:deep(.t-dropdown-item__footer-btn + .t-dropdown-item__footer-btn) {
margin-left: 32rpx;
}
</style>

View File

@@ -0,0 +1,71 @@
/* eslint-disable */
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
import type { TdDropdownItemProps } from './type';
export default {
/** 是否禁用操作项 */
disabled: Boolean,
/** 用来定义 value / label / disabled 在 `options` 中对应的字段别名 */
keys: {
type: Object,
},
/** 标题 */
label: {
type: String,
default: '',
},
/** 是否多选 */
multiple: Boolean,
/** 选项数据 */
options: {
type: Array,
default: (): TdDropdownItemProps['options'] => [],
},
/** 选项分栏1-3 */
optionsColumns: {
type: [String, Number],
default: 1 as TdDropdownItemProps['optionsColumns'],
},
/** 复选框和内容相对位置,仅单选菜单栏有效 */
placement: {
type: String,
default: 'left' as TdDropdownItemProps['placement'],
validator(val: TdDropdownItemProps['placement']): boolean {
if (!val) return true;
return ['left', 'right'].includes(val);
},
},
/** 选中值 */
value: {
type: [String, Number, Array],
default: undefined as TdDropdownItemProps['value'],
},
/** 选中值,非受控属性 */
defaultValue: {
type: [String, Number, Array],
default: undefined as TdDropdownItemProps['defaultValue'],
},
/** 值改变时触发 */
onChange: {
type: Function,
default: () => ({}),
},
/** 关闭时触发 */
onClose: {
type: Function,
default: () => ({}),
},
/** 点击确认时触发 */
onConfirm: {
type: Function,
default: () => ({}),
},
/** 点击重置时触发 */
onReset: {
type: Function,
default: () => ({}),
},
};

View File

@@ -0,0 +1,76 @@
/* eslint-disable */
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
import type { KeysType } from '../common/common';
export interface TdDropdownItemProps {
/**
* 是否禁用操作项
* @default false
*/
disabled?: boolean;
/**
* 用来定义 value / label / disabled 在 `options` 中对应的字段别名
*/
keys?: KeysType;
/**
* 标题
* @default ''
*/
label?: string;
/**
* 是否多选
* @default false
*/
multiple?: boolean;
/**
* 选项数据
* @default []
*/
options?: Array<DropdownOption>;
/**
* 选项分栏1-3
* @default 1
*/
optionsColumns?: string | number;
/**
* 复选框和内容相对位置,仅单选菜单栏有效
* @default left
*/
placement?: 'left' | 'right';
/**
* 选中值
*/
value?: DropdownValue;
/**
* 选中值,非受控属性
*/
defaultValue?: DropdownValue;
/**
* 值改变时触发
*/
onChange?: (context: { value: DropdownValue }) => void;
/**
* 关闭时触发
*/
onClose?: () => void;
/**
* 点击确认时触发
*/
onConfirm?: (context: { value: DropdownValue }) => void;
/**
* 点击重置时触发
*/
onReset?: () => void;
}
export interface DropdownOption {
label: string;
disabled: boolean;
value: DropdownValue;
}
export type DropdownValue = string | number | Array<DropdownValue>;