fix
This commit is contained in:
parent
0ca74d9787
commit
5545291a92
|
|
@ -1,3 +1,5 @@
|
|||
@import "../../miniprogram_npm/tdesign/common/style/_variables.less";
|
||||
|
||||
@import "./_theme.less";
|
||||
|
||||
// Color
|
||||
|
|
|
|||
|
|
@ -3,81 +3,47 @@
|
|||
<view class="col">
|
||||
<t-input placeholder="请输入" label="slot" size="medium" confirm-type="next" oak:value="nickname" bind:change="setValue">
|
||||
<text slot="label">
|
||||
<text style="color: #e34d59"> *</text>昵称
|
||||
<text style="color: #e34d59">*</text>昵称
|
||||
</text>
|
||||
</t-input>
|
||||
<t-input placeholder="请输入" label="slot" size="medium" confirm-type="next" oak:value="name" bind:change="setValue">
|
||||
<text slot="label">
|
||||
<text style="color: #e34d59"> *</text>姓名
|
||||
<text style="color: #e34d59">*</text>姓名
|
||||
</text>
|
||||
</t-input>
|
||||
<view class="pannel-item" bind:tap="onClickPicker" data-key="{{PICKER_KEY.SEX}}">
|
||||
<view class="pannel-label">
|
||||
<text style="color: #e34d59"> *</text>性别
|
||||
<text style="color: #e34d59">*</text>性别
|
||||
</view>
|
||||
<view class="pannel-text">{{genderText || '选择性别'}}</view>
|
||||
<t-icon name="chevron-right" color="rgba(0, 0, 0, 0.26)" size="24px" />
|
||||
</view>
|
||||
<view class="pannel-item" bind:tap="onClickPicker" data-key="{{PICKER_KEY.IDCARD}}">
|
||||
<view class="pannel-label">
|
||||
<text style="color: #e34d59"> *</text>证件类别
|
||||
<text style="color: #e34d59">*</text>证件类别
|
||||
</view>
|
||||
<view class="pannel-text">{{idCardTypeText || '选择证件类别'}}</view>
|
||||
<t-icon name="chevron-right" color="rgba(0, 0, 0, 0.26)" size="24px" />
|
||||
</view>
|
||||
<view class="pannel-item" bind:tap="onClickPicker" data-key="{{PICKER_KEY.BIRTH}}">
|
||||
<view class="pannel-label">
|
||||
<text style="color: #e34d59"> *</text>出生日期
|
||||
<text style="color: #e34d59">*</text>出生日期
|
||||
</view>
|
||||
<view class="pannel-text">{{birthText || '选择日期'}}</view>
|
||||
<t-icon name="chevron-right" color="rgba(0, 0, 0, 0.26)" size="24px" />
|
||||
</view>
|
||||
<t-picker
|
||||
data-key="{{PICKER_KEY.SEX}}"
|
||||
visible="{{sexVisible}}"
|
||||
oak:value="gender"
|
||||
title="选择性别"
|
||||
cancelBtn="取消"
|
||||
confirmBtn="确认"
|
||||
bindchange="setValue"
|
||||
bindcancel="onPickerClose"
|
||||
bindconfirm="onPickerClose"
|
||||
>
|
||||
<t-picker data-key="{{PICKER_KEY.SEX}}" visible="{{sexVisible}}" oak:value="gender" title="选择性别" cancelBtn="取消" confirmBtn="确认" bindchange="setValue" bindcancel="onPickerClose" bindconfirm="onPickerClose">
|
||||
<t-picker-item options="{{GenderOptions}}"></t-picker-item>
|
||||
</t-picker>
|
||||
<t-picker
|
||||
data-key="{{PICKER_KEY.IDCARD}}"
|
||||
visible="{{idCardVisible}}"
|
||||
oak:value="idCardType"
|
||||
title="选择证件类型"
|
||||
cancelBtn="取消"
|
||||
confirmBtn="确认"
|
||||
bindchange="setValue"
|
||||
bindcancel="onPickerClose"
|
||||
bindconfirm="onPickerClose"
|
||||
>
|
||||
<t-picker data-key="{{PICKER_KEY.IDCARD}}" visible="{{idCardVisible}}" oak:value="idCardType" title="选择证件类型" cancelBtn="取消" confirmBtn="确认" bindchange="setValue" bindcancel="onPickerClose" bindconfirm="onPickerClose">
|
||||
<t-picker-item options="{{IDCardTypeOptions}}"></t-picker-item>
|
||||
</t-picker>
|
||||
<t-input placeholder="请输入" label="slot" size="medium" type="number" confirm-type="done" oak:value="idNumber" bind:change="setValue">
|
||||
<text slot="label">
|
||||
<text style="color: #e34d59"> *</text>证件号
|
||||
<text style="color: #e34d59">*</text>证件号
|
||||
</text>
|
||||
</t-input>
|
||||
<t-date-time-picker
|
||||
title="选择日期和时间"
|
||||
data-key="{{PICKER_KEY.BIRTH}}"
|
||||
data-attr="birth"
|
||||
visible="{{birthVisible}}"
|
||||
default-value="{{today}}"
|
||||
mode="date"
|
||||
value="{{birthText}}"
|
||||
format="YYYY-MM-DD"
|
||||
bindchange="setValue"
|
||||
bindcancel="onPickerClose"
|
||||
start="{{oldestBirthday}}"
|
||||
end="{{today}}"
|
||||
>
|
||||
</t-date-time-picker>
|
||||
<t-date-time-picker title="选择日期和时间" data-key="{{PICKER_KEY.BIRTH}}" data-attr="birth" visible="{{birthVisible}}" default-value="{{today}}" mode="date" value="{{birthText}}" format="YYYY-MM-DD" bindchange="setValue" bindcancel="onPickerClose" start="{{oldestBirthday}}" end="{{today}}"></t-date-time-picker>
|
||||
</view>
|
||||
<view style="flex: 1" />
|
||||
<t-button theme="primary" disabled="{{oakExecuting || !oakDirty}}" style="margin: 16rpx" loading="{{oakExecuting}}" block size="large" bind:tap="confirm" content="确定" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"navigationBarTitleText": "修改权限",
|
||||
"usingComponents": {
|
||||
"t-checkbox": "../../../miniprogram_npm/tdesign/checkbox/checkbox",
|
||||
"t-checkbox-group": "../../../miniprogram_npm/tdesign/checkbox-group/checkbox-group",
|
||||
"t-tag": "../../../miniprogram_npm/tdesign/tag/tag",
|
||||
"t-icon": "../../../miniprogram_npm/tdesign/icon/icon",
|
||||
"t-divider": "../../../miniprogram_npm/tdesign/divider/divider",
|
||||
"t-button": "../../../miniprogram_npm/tdesign/button/button"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/** index.wxss **/
|
||||
@import "../../../config/styles/_base.less";
|
||||
@import "../../../config/styles/_mixins.less";
|
||||
|
||||
page {
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
.safe-area-inset-bottom();
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: @spacer-2;
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
margin-left: @spacer-2;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
margin-right: @spacer-1;
|
||||
font-size: @font-size-l;
|
||||
}
|
||||
.mobile {
|
||||
font-size: @font-size-base;
|
||||
color: @text-level-3-color;
|
||||
}
|
||||
.avatar {
|
||||
height: 140rpx;
|
||||
width: 140rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.img-view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 140rpx;
|
||||
width: 140rpx;
|
||||
border-radius: 50%;
|
||||
background-color: @bg-color;
|
||||
}
|
||||
.row2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: @spacer-1;
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import { firstLetterUpperCase } from "oak-domain/lib/utils/string";
|
||||
import { composeFileUrl } from "../../../../src/utils/extraFile";
|
||||
|
||||
export default OakPage(
|
||||
{
|
||||
path: 'userRelation:detail',
|
||||
entity: 'user',
|
||||
projection: async ({ props }) => {
|
||||
const { entity, entityId } = props;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
return {
|
||||
id: 1,
|
||||
name: 1,
|
||||
nickname: 1,
|
||||
mobile$user: {
|
||||
$entity: 'mobile',
|
||||
data: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
},
|
||||
[`user${entityStr}$user`]: {
|
||||
$entity: `user${entityStr}`,
|
||||
data: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
[`${entity}Id`]: 1,
|
||||
relation: 1,
|
||||
},
|
||||
filter: {
|
||||
[`${entity}Id`]: entityId,
|
||||
}
|
||||
},
|
||||
extraFile$entity: {
|
||||
$entity: 'extraFile',
|
||||
data: {
|
||||
id: 1,
|
||||
tag1: 1,
|
||||
origin: 1,
|
||||
bucket: 1,
|
||||
objectId: 1,
|
||||
filename: 1,
|
||||
extra1: 1,
|
||||
type: 1,
|
||||
entity: 1,
|
||||
extension: 1,
|
||||
},
|
||||
filter: {
|
||||
tag1: 'avatar',
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
},
|
||||
};
|
||||
},
|
||||
isList: false,
|
||||
formData: async ({ data: user, props }) => {
|
||||
const { entity, relations } = props;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
const relationArr: Array<any> = [];
|
||||
const {
|
||||
id,
|
||||
nickname,
|
||||
idState,
|
||||
userState,
|
||||
name,
|
||||
mobile$user,
|
||||
extraFile$entity,
|
||||
} = user || {};
|
||||
let userRelations = user && user[`user${entityStr}$user`] as Array<any>;
|
||||
userRelations = userRelations?.map(ele => ele.relation);
|
||||
relations.forEach(ele => {
|
||||
relationArr.push({
|
||||
checked: userRelations.includes(ele),
|
||||
value: ele,
|
||||
});
|
||||
})
|
||||
const mobile = mobile$user && mobile$user[0]?.mobile;
|
||||
const avatar =
|
||||
extraFile$entity &&
|
||||
extraFile$entity[0] &&
|
||||
composeFileUrl(extraFile$entity[0]);
|
||||
return {
|
||||
id,
|
||||
nickname,
|
||||
name,
|
||||
mobile,
|
||||
avatar,
|
||||
userState,
|
||||
idState,
|
||||
relationArr,
|
||||
};
|
||||
},
|
||||
properties: {
|
||||
entity: String,
|
||||
entityId: String,
|
||||
relations: Array,
|
||||
},
|
||||
data: {
|
||||
stateColor: {
|
||||
shadow: 'primary',
|
||||
normal: 'success',
|
||||
disabled: ''
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(event: any) {
|
||||
const { entity, entityId } = this.props;
|
||||
const { value } = event.currentTarget.dataset.value;
|
||||
const { checked } = event.detail;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
const nodeData = {
|
||||
[`${entity}Id`]: entityId,
|
||||
relation: value,
|
||||
}
|
||||
this.toggleNode(nodeData, checked, `user${entityStr}$user`);
|
||||
},
|
||||
async onConfirm() {
|
||||
await this.execute('grant');
|
||||
await this.navigateBack();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Input, Card, Avatar } from 'antd';
|
||||
const Search = Input.Search;
|
||||
const Meta = Card.Meta;
|
||||
|
||||
export default function render() {
|
||||
const { rows } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Search
|
||||
placeholder="请输入"
|
||||
value={this.state.searchValue || ''}
|
||||
enterButton="搜索"
|
||||
size="middle"
|
||||
loading={this.state.oakLoading}
|
||||
onChange={this.searchChange}
|
||||
allowClear
|
||||
onSearch={(value, event) => {
|
||||
// value清空
|
||||
if (value) {
|
||||
this.searchConfirm();
|
||||
} else {
|
||||
this.searchCancel();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{rows?.map((item, index) => {
|
||||
<Card key={index} onClick={this.handleCardClick}>
|
||||
<Meta title={item.name} />
|
||||
</Card>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<!-- index.wxml -->
|
||||
<view class="container">
|
||||
<view class="content">
|
||||
<view class="row">
|
||||
<block wx:if="{{avatar}}">
|
||||
<image class="avatar" src="{{avatar}}" mode="aspectFit" />
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="img-view">
|
||||
<t-icon name="icon" size="xl" />
|
||||
</view>
|
||||
</block>
|
||||
<view class="col">
|
||||
<view class="row2">
|
||||
<text class="nickname">{{nickname || '未设置'}}</text>
|
||||
<t-tag theme="{{stateColor[userState]}}" shape="round">{{userState}}</t-tag>
|
||||
</view>
|
||||
<text class="mobile">姓名: {{name || '未设置'}}</text>
|
||||
<text class="mobile">手机: {{mobile || '未设置'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<t-divider t-class="demo-1" t-class-content="t-class-content">
|
||||
<text slot="content">权限设置</text>
|
||||
</t-divider>
|
||||
<block wx:for="{{relationArr}}">
|
||||
<t-checkbox data-value="{{item}}" checked="{{item.checked}}" label="{{item.value}}" bind:change="onChange" />
|
||||
</block>
|
||||
</view>
|
||||
<view class=""></view>
|
||||
<t-button theme="primary" bind:tap="onConfirm" block>保存</t-button>
|
||||
</view>
|
||||
|
|
@ -107,11 +107,12 @@ export default OakPage(
|
|||
},
|
||||
lifetimes: {},
|
||||
methods: {
|
||||
|
||||
onRemove(event: any) {
|
||||
const { index } = event.target.dataset;
|
||||
this.setState({
|
||||
show: true,
|
||||
deleteIndex: index,
|
||||
deleteIndex: Number(index),
|
||||
})
|
||||
},
|
||||
cancelDelete() {
|
||||
|
|
@ -124,7 +125,7 @@ export default OakPage(
|
|||
const { entity } = this.props;
|
||||
const entityStr = firstLetterUpperCase(entity);
|
||||
const { deleteIndex } = this.state;
|
||||
typeof deleteIndex === 'number' && this.toggleNode({}, false, `${deleteIndex}.user${entityStr}$user`);
|
||||
typeof deleteIndex === 'number' && this.removeNode(`user.user${entityStr}$user`, deleteIndex);
|
||||
this.setState({
|
||||
show: false,
|
||||
deleteIndex: '',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"navigationBarTitleText": "修改权限",
|
||||
"usingComponents": {
|
||||
"t-button": "../../../miniprogram_npm/tdesign/button/button",
|
||||
"t-dialog": "../../../miniprogram_npm/tdesign/dialog/dialog",
|
||||
"t-divider": "../../../miniprogram_npm/tdesign/divider/divider",
|
||||
"t-tag": "../../../miniprogram_npm/tdesign/tag/tag",
|
||||
"t-fab": "../../../miniprogram_npm/tdesign/fab/fab",
|
||||
"t-icon": "../../../miniprogram_npm/tdesign/icon/icon",
|
||||
"fab": "../../../components/UI/fab/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/** index.wxss **/
|
||||
@import "../../../config/styles/_base.less";
|
||||
@import "../../../config/styles/_mixins.less";
|
||||
|
||||
page {
|
||||
height: 100%;
|
||||
background-color: @bg-color;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
.safe-area-inset-bottom();
|
||||
}
|
||||
|
||||
.card-view {
|
||||
margin: @spacer-2;
|
||||
margin-bottom: 0rpx;
|
||||
padding: @spacer-2;
|
||||
border-radius: 4rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 20rpx 0 rgba(212, 217, 223, 0.5);
|
||||
background-color: #fff;
|
||||
}
|
||||
.row {
|
||||
margin-left: @spacer-2;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
margin-bottom: 8rpx;
|
||||
font-size: @font-size-l;
|
||||
}
|
||||
.mobile {
|
||||
font-size: @font-size-base;
|
||||
color: @text-level-2-color;
|
||||
}
|
||||
.avatar {
|
||||
height: 140rpx;
|
||||
width: 140rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.img-view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 140rpx;
|
||||
width: 140rpx;
|
||||
border-radius: 50%;
|
||||
background-color: @bg-color;
|
||||
}
|
||||
.realtion {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
font-size: @font-size-base;
|
||||
color: @text-level-2-color;
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
import React, { useState, useMemo, useRef, useEffect } from 'react';
|
||||
import { Table, Input, Select, DatePicker, MessagePlugin, Button } from 'tdesign-react';
|
||||
|
||||
export default function render() {
|
||||
|
||||
function setEditableRowKeys(arr: Array<string>){
|
||||
this.setState({
|
||||
editableRowKeys: arr,
|
||||
})
|
||||
};
|
||||
function init() {
|
||||
const initData = new Array(5).fill(null).map((_, i) => ({
|
||||
key: String(i + 1),
|
||||
firstName: ['Eric', 'Gilberta', 'Heriberto', 'Lazarus', 'Zandra'][i % 4],
|
||||
framework: ['Vue', 'React', 'Miniprogram', 'Flutter'][i % 4],
|
||||
email: [
|
||||
'espinke0@apache.org',
|
||||
'gpurves1@issuu.com',
|
||||
'hkment2@nsw.gov.au',
|
||||
'lskures3@apache.org',
|
||||
'zcroson5@virginia.edu',
|
||||
][i % 4],
|
||||
letters: [['A'], ['B', 'E'], ['C'], ['D', 'G', 'H']][i % 4],
|
||||
createTime: ['2021-11-01', '2021-12-01', '2022-01-01', '2022-02-01', '2022-03-01'][i % 4],
|
||||
}));
|
||||
this.setState({
|
||||
data: [...initData],
|
||||
});
|
||||
setEditableRowKeys(['1']);
|
||||
}
|
||||
|
||||
let currentSaveId = '';
|
||||
// 保存变化过的行信息
|
||||
const editMap = {};
|
||||
|
||||
const onEdit = (e) => {
|
||||
const { editableRowKeys } = this.state;
|
||||
const { id } = e.currentTarget.dataset;
|
||||
if (!editableRowKeys.includes(id)) {
|
||||
setEditableRowKeys(editableRowKeys.concat(id));
|
||||
}
|
||||
};
|
||||
|
||||
// 更新 editableRowKeys
|
||||
const updateEditState = (id) => {
|
||||
const { editableRowKeys } = this.state;
|
||||
const index = editableRowKeys.findIndex((t) => t === id);
|
||||
editableRowKeys.splice(index, 1);
|
||||
setEditableRowKeys([...editableRowKeys]);
|
||||
};
|
||||
|
||||
const onCancel = (e) => {
|
||||
const { id } = e.currentTarget.dataset;
|
||||
updateEditState(id);
|
||||
this.tableRef.current.clearValidateData();
|
||||
};
|
||||
|
||||
const onSave = (e) => {
|
||||
const { id } = e.currentTarget.dataset;
|
||||
currentSaveId = id;
|
||||
// 触发内部校验,而后在 onRowValidate 中接收异步校验结果
|
||||
this.tableRef.current.validateRowData(id);
|
||||
};
|
||||
|
||||
const onRowValidate = (params) => {
|
||||
console.log('validate:', params);
|
||||
const { data } = this.state;
|
||||
if (params.result.length) {
|
||||
const r = params.result[0];
|
||||
MessagePlugin.error(`${r.col.title} ${r.errorList[0].message}`);
|
||||
return;
|
||||
}
|
||||
// 如果是 table 的父组件主动触发校验
|
||||
if (params.trigger === 'parent' && !params.result.length) {
|
||||
const current = editMap[currentSaveId];
|
||||
if (current) {
|
||||
data.splice(current.rowIndex, 1, current.editedRow);
|
||||
this.setState({
|
||||
data: [...data],
|
||||
})
|
||||
MessagePlugin.success('保存成功');
|
||||
}
|
||||
updateEditState(currentSaveId);
|
||||
}
|
||||
};
|
||||
|
||||
const onRowEdit = (params) => {
|
||||
const { row, col, value } = params;
|
||||
const oldRowData = editMap[row.key]?.editedRow || row;
|
||||
editMap[row.key] = {
|
||||
...params,
|
||||
editedRow: { ...oldRowData, [col.colKey]: value },
|
||||
};
|
||||
};
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'FirstName',
|
||||
colKey: 'firstName',
|
||||
align: 'left',
|
||||
// 编辑状态相关配置,全部集中在 edit
|
||||
edit: {
|
||||
// 1. 支持任意组件。需保证组件包含 `value` 和 `onChange` 两个属性,且 onChange 的第一个参数值为 new value。
|
||||
// 2. 如果希望支持校验,组件还需包含 `status` 和 `tips` 属性。具体 API 含义参考 Input 组件
|
||||
component: Input,
|
||||
// props, 透传全部属性到 Input 组件
|
||||
props: {
|
||||
clearable: true,
|
||||
autofocus: true,
|
||||
autoWidth: true,
|
||||
},
|
||||
// 校验规则,此处同 Form 表单
|
||||
rules: [
|
||||
{ required: true, message: '不能为空' },
|
||||
{ max: 10, message: '字符数量不能超过 10', type: 'warning' },
|
||||
],
|
||||
showEditIcon: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Framework',
|
||||
colKey: 'framework',
|
||||
edit: {
|
||||
component: Select,
|
||||
// props, 透传全部属性到 Select 组件
|
||||
props: {
|
||||
clearable: true,
|
||||
autoWidth: true,
|
||||
options: [
|
||||
{ label: 'Vue', value: 'Vue' },
|
||||
{ label: 'React', value: 'React' },
|
||||
{ label: 'Miniprogram', value: 'Miniprogram' },
|
||||
{ label: 'Flutter', value: 'Flutter' },
|
||||
],
|
||||
},
|
||||
showEditIcon: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Letters',
|
||||
colKey: 'letters',
|
||||
cell: ({ row }) => row?.letters?.join('、'),
|
||||
edit: {
|
||||
component: Select,
|
||||
// props, 透传全部属性到 Select 组件
|
||||
// props 为函数时,参数有:col, row, rowIndex, colIndex, editedRow。一般用于实现编辑组件之间的联动
|
||||
props: ({ editedRow }) => ({
|
||||
multiple: true,
|
||||
minCollapsedNum: 1,
|
||||
autoWidth: true,
|
||||
options: [
|
||||
{ label: 'A', value: 'A' },
|
||||
{ label: 'B', value: 'B' },
|
||||
{ label: 'C', value: 'C' },
|
||||
{ label: 'D', value: 'D' },
|
||||
{ label: 'E', value: 'E' },
|
||||
// 如果框架选择了 React,则 Letters 隐藏 G 和 H
|
||||
{ label: 'G', value: 'G', show: () => editedRow.framework !== 'React' },
|
||||
{ label: 'H', value: 'H', show: () => editedRow.framework !== 'React' },
|
||||
].filter(t => (t.show === undefined ? true : t.show())),
|
||||
}),
|
||||
showEditIcon: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Date',
|
||||
colKey: 'createTime',
|
||||
className: 't-demo-col__datepicker',
|
||||
// props, 透传全部属性到 DatePicker 组件
|
||||
edit: {
|
||||
component: DatePicker,
|
||||
showEditIcon: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Operate',
|
||||
colKey: 'operate',
|
||||
width: 150,
|
||||
cell: ({ row }) => {
|
||||
const {}
|
||||
const editable = editableRowKeys.includes(row.key);
|
||||
return (
|
||||
<div className="table-operations">
|
||||
{!editable && (
|
||||
<Button theme="primary" variant="text" data-id={row.key} onClick={onEdit}>
|
||||
编辑
|
||||
</Button>
|
||||
)}
|
||||
{editable && (
|
||||
<Button theme="primary" variant="text" data-id={row.key} onClick={onSave}>
|
||||
保存
|
||||
</Button>
|
||||
)}
|
||||
{editable && (
|
||||
<Button theme="primary" variant="text" data-id={row.key} onClick={onCancel}>
|
||||
取消
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}
|
||||
],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[data, editableRowKeys],
|
||||
);
|
||||
|
||||
// 当前示例包含:输入框、单选、多选、日期 等场景
|
||||
return (
|
||||
<div className="t-table-demo__editable-row">
|
||||
<Table
|
||||
ref={tableRef}
|
||||
rowKey="key"
|
||||
columns={columns}
|
||||
data={data}
|
||||
editableRowKeys={editableRowKeys}
|
||||
onRowEdit={onRowEdit}
|
||||
onRowValidate={onRowValidate}
|
||||
bordered
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
import { firstLetterUpperCase } from 'oak-domain/lib/utils/string';
|
||||
import { composeFileUrl } from '../../../../src/utils/extraFile';
|
||||
import React from 'react';
|
||||
|
||||
export default OakPage(
|
||||
{
|
||||
path: 'userRelation:list',
|
||||
entity: 'user',
|
||||
projection: async ({ props }) => {
|
||||
const { entity, entityId, relation } = props;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
return {
|
||||
id: 1,
|
||||
name: 1,
|
||||
nickname: 1,
|
||||
mobile$user: {
|
||||
$entity: 'mobile',
|
||||
data: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
},
|
||||
[`user${entityStr}$user`]: {
|
||||
$entity: `user${entityStr}`,
|
||||
data: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
[`${entity}Id`]: 1,
|
||||
relation: 1,
|
||||
},
|
||||
},
|
||||
extraFile$entity: {
|
||||
$entity: 'extraFile',
|
||||
data: {
|
||||
id: 1,
|
||||
tag1: 1,
|
||||
origin: 1,
|
||||
bucket: 1,
|
||||
objectId: 1,
|
||||
filename: 1,
|
||||
extra1: 1,
|
||||
type: 1,
|
||||
entity: 1,
|
||||
extension: 1,
|
||||
},
|
||||
filter: {
|
||||
tag1: 'avatar',
|
||||
},
|
||||
indexFrom: 0,
|
||||
count: 1,
|
||||
},
|
||||
};
|
||||
},
|
||||
filters: [
|
||||
// 由调用者注入oakFilter
|
||||
{
|
||||
filter: async ({ features, props, onLoadOptions }) => {
|
||||
const { entityId, entity } = props;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
return {
|
||||
id: {
|
||||
$in: {
|
||||
entity: `user${entityStr}`,
|
||||
data: {
|
||||
userId: 1,
|
||||
},
|
||||
filter: {
|
||||
[`${entity}Id`]: entityId,
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
isList: true,
|
||||
formData: async function ({ data: users, props, features }) {
|
||||
const { entity } = props;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
const filter = await this.getFilterByName('name');
|
||||
return {
|
||||
users: users?.map((ele: any) => {
|
||||
const { mobile$user, extraFile$entity, [`user${entityStr}$user`]: userEntities } =
|
||||
ele || {};
|
||||
const mobile = mobile$user && mobile$user[0]?.mobile;
|
||||
const avatar =
|
||||
extraFile$entity &&
|
||||
extraFile$entity[0] &&
|
||||
composeFileUrl(extraFile$entity[0]);
|
||||
const user2 = Object.assign({}, ele, {
|
||||
mobile,
|
||||
avatar,
|
||||
relations: userEntities,
|
||||
hasRelation: userEntities.length > 0,
|
||||
});
|
||||
return user2;
|
||||
}),
|
||||
};
|
||||
},
|
||||
properties: {
|
||||
entity: String,
|
||||
entityId: String,
|
||||
userIds: Array,
|
||||
relations: Array,
|
||||
},
|
||||
data: {
|
||||
show: false,
|
||||
searchValue: '',
|
||||
deleteIndex: '',
|
||||
},
|
||||
lifetimes: {
|
||||
created() {
|
||||
if (process.env.OAK_PLATFORM === 'web') {
|
||||
const classStyles = `
|
||||
<style>
|
||||
.t-table-demo__editable-row .table-operations > button {
|
||||
padding: 0 8px;
|
||||
line-height: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.t-table-demo__editable-row .t-demo-col__datepicker .t-date-picker {
|
||||
width: 120px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
this.tableRef = React.createRef();
|
||||
document.head.insertAdjacentHTML('beforeend', classStyles);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goDetail(e: any) {
|
||||
const { relations, entity, entityId } = this.props;
|
||||
const { id } = e.currentTarget.dataset;
|
||||
this.navigateTo({
|
||||
url: '/userRelation/detail2',
|
||||
oakId: id,
|
||||
relations,
|
||||
entity,
|
||||
entityId,
|
||||
})
|
||||
},
|
||||
goUpsert() {
|
||||
const { entity, entityId, relations } = this.props;
|
||||
this.navigateTo({
|
||||
url: '/userRelation/upsert',
|
||||
entity,
|
||||
entityId,
|
||||
relations,
|
||||
})
|
||||
},
|
||||
onRemove(event: any) {
|
||||
const { index } = event.target.dataset;
|
||||
this.setState({
|
||||
show: true,
|
||||
deleteIndex: Number(index),
|
||||
})
|
||||
},
|
||||
cancelDelete() {
|
||||
this.setState({
|
||||
show: false,
|
||||
deleteIndex: '',
|
||||
})
|
||||
},
|
||||
confirmDelete() {
|
||||
const { entity } = this.props;
|
||||
const entityStr = firstLetterUpperCase(entity);
|
||||
const { deleteIndex } = this.state;
|
||||
typeof deleteIndex === 'number' && this.removeNode(`user.user${entityStr}$user`, deleteIndex);
|
||||
this.setState({
|
||||
show: false,
|
||||
deleteIndex: '',
|
||||
})
|
||||
},
|
||||
onAdd(event: any) {
|
||||
const { entity, entityId, relation } = this.props;
|
||||
const entityStr = firstLetterUpperCase(entity);
|
||||
const { index } = event.target.dataset;
|
||||
this.toggleNode({
|
||||
[`${entity}Id`]: entityId,
|
||||
relation,
|
||||
}, true, `${index}.user${entityStr}$user`);
|
||||
},
|
||||
async confirm() {
|
||||
await this.execute();
|
||||
this.navigateBack();
|
||||
},
|
||||
goSearch() {
|
||||
this.navigateTo({
|
||||
url: '/user/search',
|
||||
toUrl: '/userRelation/detail'
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export default function render() {
|
||||
return (
|
||||
<div>
|
||||
userList
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!-- index.wxml -->
|
||||
<view class="container">
|
||||
<view class="col">
|
||||
<block wx:for="{{users}}" wx:key="index">
|
||||
<view class="card-view" data-id="{{item.id}}" bind:tap="goDetail">
|
||||
<block wx:if="{{item.avatar}}">
|
||||
<image class="avatar" src="{{item.avatar}}" mode="aspectFit" />
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="img-view">
|
||||
<t-icon name="image" size="xl" />
|
||||
</view>
|
||||
</block>
|
||||
<view class="row">
|
||||
<view class="col">
|
||||
<text class="nickname">{{item.nickname || '未设置'}}</text>
|
||||
<text class="mobile">姓名: {{item.name || '未设置'}}</text>
|
||||
<text class="mobile">手机: {{item.mobile || '未设置'}}</text>
|
||||
<view class="realtion">
|
||||
权限:
|
||||
<block wx:if="{{item.relations.length}}" wx:for="{{item.relations}}" wx:for-item="item2" wx:for-index="index2" wx:key="index2">
|
||||
<text>{{item2.relation}}</text>
|
||||
<block wx:if="{{index2 !== item.relations.length - 1}}">
|
||||
<t-divider layout="vertical"></t-divider>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<t-fab icon="add" bind:click="goUpsert" />
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"navigationBarTitleText": "修改权限",
|
||||
"usingComponents": {
|
||||
"t-checkbox": "../../../miniprogram_npm/tdesign/checkbox/checkbox",
|
||||
"t-checkbox-group": "../../../miniprogram_npm/tdesign/checkbox-group/checkbox-group",
|
||||
"t-input": "../../../miniprogram_npm/tdesign/input/input",
|
||||
"t-tag": "../../../miniprogram_npm/tdesign/tag/tag",
|
||||
"t-icon": "../../../miniprogram_npm/tdesign/icon/icon",
|
||||
"t-button": "../../../miniprogram_npm/tdesign/button/button"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/** index.wxss **/
|
||||
@import "../../../config/styles/_base.less";
|
||||
@import "../../../config/styles/_mixins.less";
|
||||
|
||||
page {
|
||||
height: 100%;
|
||||
background-color: @bg-color;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
.safe-area-inset-bottom();
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.btn-view {
|
||||
display: flex;
|
||||
margin: 0rpx @spacer-2;
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import * as React from 'react';
|
||||
import { Form, Input, Radio, Checkbox, Button, Switch, MessagePlugin, DatePicker, Tooltip, CheckboxGroup } from 'tdesign-react';
|
||||
|
||||
const { FormItem } = Form;
|
||||
export default function render() {
|
||||
const { name, mobile } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Form
|
||||
colon={false}
|
||||
labelAlign="right"
|
||||
labelWidth="100px"
|
||||
layout="vertical"
|
||||
preventSubmitDefault
|
||||
resetType="empty"
|
||||
showErrorMessage
|
||||
submitWithWarningMessage={false}
|
||||
>
|
||||
<FormItem
|
||||
label="姓名"
|
||||
name="name"
|
||||
successBorder={false}
|
||||
>
|
||||
<Input
|
||||
onChange={(value, context) => {
|
||||
this.setUpdateData('name', value);
|
||||
}}
|
||||
value={name}
|
||||
align="left"
|
||||
autoWidth={false}
|
||||
autofocus={false}
|
||||
clearable={false}
|
||||
defaultValue=""
|
||||
placeholder="请输入内容"
|
||||
readonly={false}
|
||||
showClearIconOnEmpty={false}
|
||||
size="medium"
|
||||
status="default"
|
||||
type="text"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="手机号码"
|
||||
name="mobile"
|
||||
successBorder={false}
|
||||
>
|
||||
<Input
|
||||
value={mobile}
|
||||
align="left"
|
||||
autoWidth={false}
|
||||
autofocus={false}
|
||||
clearable={false}
|
||||
defaultValue=""
|
||||
placeholder="请输入内容"
|
||||
readonly={false}
|
||||
showClearIconOnEmpty={false}
|
||||
size="medium"
|
||||
status="default"
|
||||
type="text"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
initialData={[
|
||||
'1'
|
||||
]}
|
||||
label="课程"
|
||||
name="course"
|
||||
successBorder={false}
|
||||
>
|
||||
<Checkbox.Group
|
||||
defaultValue={[]}
|
||||
max={undefined}
|
||||
>
|
||||
<Checkbox
|
||||
checkAll={false}
|
||||
defaultChecked={false}
|
||||
disabled={undefined}
|
||||
indeterminate={false}
|
||||
readonly={false}
|
||||
value="1"
|
||||
>
|
||||
语文
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checkAll={false}
|
||||
defaultChecked={false}
|
||||
disabled={undefined}
|
||||
indeterminate={false}
|
||||
readonly={false}
|
||||
value="2"
|
||||
>
|
||||
数学
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checkAll={false}
|
||||
defaultChecked={false}
|
||||
disabled={undefined}
|
||||
indeterminate={false}
|
||||
readonly={false}
|
||||
value="3"
|
||||
>
|
||||
英语
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
checkAll={false}
|
||||
defaultChecked={false}
|
||||
disabled={undefined}
|
||||
indeterminate={false}
|
||||
readonly={false}
|
||||
value="4"
|
||||
>
|
||||
体育
|
||||
</Checkbox>
|
||||
</Checkbox.Group>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import { firstLetterUpperCase } from "oak-domain/lib/utils/string";
|
||||
import { composeFileUrl } from "../../../../src/utils/extraFile";
|
||||
|
||||
export default OakPage(
|
||||
{
|
||||
path: 'userRelation:upsert',
|
||||
entity: 'user',
|
||||
projection: async ({ props }) => {
|
||||
const { entity, entityId, relation } = props;
|
||||
const entityStr = firstLetterUpperCase(entity!);
|
||||
return {
|
||||
id: 1,
|
||||
name: 1,
|
||||
password: 1,
|
||||
mobile$user: {
|
||||
$entity: 'mobile',
|
||||
data: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
mobile: 1,
|
||||
},
|
||||
},
|
||||
[`user${entityStr}$user`]: {
|
||||
$entity: `user${entityStr}`,
|
||||
data: {
|
||||
id: 1,
|
||||
userId: 1,
|
||||
[`${entity}Id`]: 1,
|
||||
relation: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
isList: false,
|
||||
formData: async ({ data: user, props }) => {
|
||||
const { entity, relations } = props;
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
mobile$user,
|
||||
password,
|
||||
} = user || {};
|
||||
const mobile = mobile$user && mobile$user[0]?.mobile;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
mobile,
|
||||
password,
|
||||
};
|
||||
},
|
||||
properties: {
|
||||
entity: String,
|
||||
entityId: String,
|
||||
relations: Array,
|
||||
},
|
||||
data: {
|
||||
mobile: '',
|
||||
},
|
||||
methods: {
|
||||
setValue(input: any) {
|
||||
const { dataset, value, Context } = this.resolveInput(input);
|
||||
this.setUpdateData(dataset!.attr, value);
|
||||
},
|
||||
onMobileChange(event:any) {
|
||||
const { value } = event.detail;
|
||||
this.setState({
|
||||
mobile: value,
|
||||
})
|
||||
this.setUpdateData('mobile$user.0.mobile', value);
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
import * as React from 'react';
|
||||
|
||||
export default function render() {
|
||||
return (
|
||||
<div>
|
||||
userUpsertMobile
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!-- index.wxml -->
|
||||
<view class="container">
|
||||
<!-- index.wxml -->
|
||||
<view class="page-body">
|
||||
<view class="col">
|
||||
<t-input placeholder="请输入" label="slot" size="medium" confirm-type="done" oak:value="name" bind:change="setValue">
|
||||
<text slot="label">
|
||||
<text style="color: #e34d59">*</text>姓名
|
||||
</text>
|
||||
</t-input>
|
||||
<t-input placeholder="请输入" label="slot" size="medium" confirm-type="next" value="{{mobile}}" bind:change="onMobileChange">
|
||||
<text slot="label">
|
||||
<text style="color: #e34d59">*</text>手机号
|
||||
</text>
|
||||
</t-input>
|
||||
<t-input placeholder="请输入" label="slot" size="medium" confirm-type="next" oak:value="password" bind:change="setValue">
|
||||
<text slot="label">
|
||||
<text style="color: #e34d59">*</text>密码
|
||||
</text>
|
||||
</t-input>
|
||||
<t-checkbox-group bind:change="onCheckBoxChange">
|
||||
<block wx:for="{{relations}}">
|
||||
<t-checkbox value="{{item}}" label="{{item}}" />
|
||||
</block>
|
||||
</t-checkbox-group>
|
||||
</view>
|
||||
<view style="flex: 1" />
|
||||
<view class="btn-view">
|
||||
<t-button theme="primary" bind:tap="onConfirm" block>确定</t-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
Loading…
Reference in New Issue