diff --git a/es/components/common/tabBar/index.less b/es/components/common/tabBar/index.less index 820ddc782..b12388bc3 100644 --- a/es/components/common/tabBar/index.less +++ b/es/components/common/tabBar/index.less @@ -13,7 +13,7 @@ z-index: 999; width: 750rpx; - height: calc(106rpx + env(safe-area-inset-bottom)); + height: 106rpx; display: flex; background-color: white; background-size: 100% 100%; diff --git a/es/components/mobile/login/index.d.ts b/es/components/mobile/login/index.d.ts new file mode 100644 index 000000000..98aa9960a --- /dev/null +++ b/es/components/mobile/login/index.d.ts @@ -0,0 +1,8 @@ +/// +declare const _default: (props: import("oak-frontend-base").ReactComponentProps void) | undefined; +}>) => import("react").ReactElement>; +export default _default; diff --git a/es/components/mobile/login/index.js b/es/components/mobile/login/index.js new file mode 100644 index 000000000..9dca8b919 --- /dev/null +++ b/es/components/mobile/login/index.js @@ -0,0 +1,119 @@ +import { LOCAL_STORAGE_KEYS } from '../../../config/constants'; +const SEND_KEY = LOCAL_STORAGE_KEYS.captchaSendAt; +const SEND_CAPTCHA_LATENCY = process.env.NODE_ENV === 'development' ? 10 : 60; +export default OakComponent({ + isList: false, + projection: { + id: 1, + mobile: 1, + userId: 1, + }, + data: { + mobile: '', + captcha: '', + counter: 0, + refreshing: false, + password: '', + }, + properties: { + onlyCaptcha: false, + onlyPassword: false, + eventLoggedIn: '', + callback: undefined, + }, + formData({ features }) { + const lastSendAt = features.localStorage.load(SEND_KEY); + const now = Date.now(); + let counter = 0; + if (typeof lastSendAt === 'number') { + counter = Math.max(SEND_CAPTCHA_LATENCY - Math.ceil((now - lastSendAt) / 1000), 0); + if (counter > 0) { + this.counterHandler = setTimeout(() => this.reRender(), 1000); + } + else if (this.counterHandler) { + clearTimeout(this.counterHandler); + this.counterHandler = undefined; + } + } + return { + counter, + }; + }, + methods: { + setMobile(value) { + this.setState({ + mobile: value, + }); + }, + setCaptcha(value) { + this.setState({ + captcha: value, + }); + }, + async sendCaptcha() { + const { mobile } = this.state; + try { + const result = await this.features.token.sendCaptcha(mobile, 'login'); + // 显示返回消息 + this.setMessage({ + type: 'success', + content: result, + }); + this.save(SEND_KEY, Date.now()); + this.reRender(); + } + catch (err) { + this.setMessage({ + type: 'error', + content: err.message, + }); + } + }, + async loginByMobile() { + const { eventLoggedIn, callback } = this.props; + const { mobile, password, captcha } = this.state; + try { + await this.features.token.loginByMobile(mobile, password, captcha); + if (typeof callback === 'function') { + callback(); + } + else if (eventLoggedIn) { + this.pubEvent(eventLoggedIn); + } + else { + this.navigateBack(); + } + } + catch (err) { + this.setMessage({ + type: 'error', + content: err.message, + }); + } + }, + async onRefreshMobile(e) { + this.setState({ + refreshing: true, + }); + try { + const { code, errMsg } = e.detail; + if (errMsg !== 'getPhoneNumber:ok') { + console.error(errMsg); + this.setMessage({ + title: '获取手机号失败', + type: 'warning', + }); + } + else { + await this.features.token.getWechatMpUserPhoneNumber(code); + } + } + catch (err) { + console.error(err); + } + this.setState({ + refreshing: false, + }); + }, + }, +}); diff --git a/src/pages/mobile/login/index.json b/es/components/mobile/login/index.json similarity index 100% rename from src/pages/mobile/login/index.json rename to es/components/mobile/login/index.json diff --git a/src/pages/mobile/login/index.less b/es/components/mobile/login/index.less similarity index 100% rename from src/pages/mobile/login/index.less rename to es/components/mobile/login/index.less diff --git a/src/pages/mobile/login/index.xml b/es/components/mobile/login/index.xml similarity index 100% rename from src/pages/mobile/login/index.xml rename to es/components/mobile/login/index.xml diff --git a/src/pages/mobile/login/locales/zh_CN.json b/es/components/mobile/login/locales/zh_CN.json similarity index 100% rename from src/pages/mobile/login/locales/zh_CN.json rename to es/components/mobile/login/locales/zh_CN.json diff --git a/src/pages/mobile/login/mobile.module.less b/es/components/mobile/login/mobile.module.less similarity index 100% rename from src/pages/mobile/login/mobile.module.less rename to es/components/mobile/login/mobile.module.less diff --git a/es/components/mobile/login/web.d.ts b/es/components/mobile/login/web.d.ts new file mode 100644 index 000000000..f20ffeaf0 --- /dev/null +++ b/es/components/mobile/login/web.d.ts @@ -0,0 +1,20 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function render(props: WebComponentProps void; + setMobile: (mobile: string) => void; + sendCaptcha: () => Promise; + loginByMobile: () => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/mobile/login/web.js b/es/components/mobile/login/web.js new file mode 100644 index 000000000..5fe2d8b76 --- /dev/null +++ b/es/components/mobile/login/web.js @@ -0,0 +1,20 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { isMobile, isCaptcha, } from 'oak-domain/lib/utils/validator'; +import { MobileOutlined } from '@ant-design/icons'; +import { Form, Input, Button } from 'antd'; +import Style from './mobile.module.less'; +export default function render(props) { + const { mobile, captcha, password, counter } = props.data; + const { t, setMobile, setCaptcha, sendCaptcha, loginByMobile } = props.methods; + const validMobile = isMobile(mobile); + const validCaptcha = isCaptcha(captcha); + const allowSubmit = validMobile && validCaptcha; + const LoginCaptcha = (_jsxs(Form, { colon: true, children: [_jsx(Form.Item, { name: "mobile", children: _jsx(Input, { allowClear: true, value: mobile, "data-attr": "mobile", type: "tel", maxLength: 11, prefix: _jsx(MobileOutlined, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: (e) => { + setMobile(e.target.value); + }, className: Style['loginbox-input'] }) }), _jsx(Form.Item, { name: "captcha", children: _jsx(Input, { allowClear: true, value: captcha, "data-attr": "captcha", + // type="number" + maxLength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: (e) => { + setCaptcha(e.target.value); + }, className: Style['loginbox-input'], suffix: _jsx(Button, { type: "link", disabled: !validMobile || counter > 0, onClick: () => sendCaptcha(), children: counter > 0 ? `${counter}秒后可重发` : t('Send') }) }) }), _jsx(Form.Item, { children: _jsx(Button, { block: true, size: "large", type: "primary", htmlType: "submit", disabled: !allowSubmit, onClick: () => loginByMobile(), children: t('Login') }) })] })); + return (_jsx("div", { className: Style['loginbox-main'], children: _jsxs("div", { className: Style['loginbox-wrap'], children: [_jsx("div", { className: Style['loginbox-hd'], children: "\u4E3A\u4E86\u66F4\u597D\u7684\u4F53\u9A8C\uFF0C\u8BF7\u7ED1\u5B9A\u624B\u673A\u53F7" }), _jsx("div", { className: Style['loginbox-bd'], children: _jsx("div", { className: Style['loginbox-mobile'], children: LoginCaptcha }) })] }) })); +} diff --git a/src/pages/mobile/login/web.module.less b/es/components/mobile/login/web.module.less similarity index 100% rename from src/pages/mobile/login/web.module.less rename to es/components/mobile/login/web.module.less diff --git a/es/components/mobile/login/web.pc.d.ts b/es/components/mobile/login/web.pc.d.ts new file mode 100644 index 000000000..bb1add491 --- /dev/null +++ b/es/components/mobile/login/web.pc.d.ts @@ -0,0 +1,19 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function render(props: WebComponentProps void; + setMobile: (mobile: string) => void; + sendCaptcha: () => Promise; + loginByMobile: () => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/mobile/login/web.pc.js b/es/components/mobile/login/web.pc.js new file mode 100644 index 000000000..b06f4200a --- /dev/null +++ b/es/components/mobile/login/web.pc.js @@ -0,0 +1,20 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { isMobile, isCaptcha, } from 'oak-domain/lib/utils/validator'; +import { MobileOutlined } from '@ant-design/icons'; +import { Form, Input, Button } from 'antd'; +import Style from './web.module.less'; +export default function render(props) { + const { mobile, captcha, counter } = props.data; + const { t, setMobile, setCaptcha, sendCaptcha, loginByMobile } = props.methods; + const validMobile = isMobile(mobile); + const validCaptcha = isCaptcha(captcha); + const allowSubmit = validMobile && validCaptcha; + const LoginCaptcha = (_jsxs(Form, { colon: true, children: [_jsx(Form.Item, { name: "mobile", children: _jsx(Input, { allowClear: true, value: mobile, "data-attr": "mobile", type: "tel", maxLength: 11, prefix: _jsx(MobileOutlined, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: (e) => { + setMobile(e.target.value); + }, className: Style['loginbox-input'] }) }), _jsx(Form.Item, { name: "captcha", children: _jsx(Input, { allowClear: true, value: captcha, "data-attr": "captcha", + // type="number" + maxLength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: (e) => { + setCaptcha(e.target.value); + }, className: Style['loginbox-input'], suffix: _jsx(Button, { type: "link", disabled: !validMobile || counter > 0, onClick: () => sendCaptcha(), children: counter > 0 ? `${counter}秒后可重发` : t('Send') }) }) }), _jsx(Form.Item, { children: _jsx(Button, { block: true, size: "large", type: "primary", htmlType: "submit", disabled: !allowSubmit, onClick: () => loginByMobile(), children: t('Login') }) })] })); + return (_jsx("div", { className: Style['loginbox-main'], children: _jsxs("div", { className: Style['loginbox-wrap'], children: [_jsx("div", { className: Style['loginbox-hd'], children: "\u4E3A\u4E86\u66F4\u597D\u7684\u4F53\u9A8C\uFF0C\u8BF7\u7ED1\u5B9A\u624B\u673A\u53F7" }), _jsx("div", { className: Style['loginbox-bd'], children: _jsx("div", { className: Style['loginbox-mobile'], children: LoginCaptcha }) })] }) })); +} diff --git a/es/components/mobile/me/index.d.ts b/es/components/mobile/me/index.d.ts new file mode 100644 index 000000000..516f8df68 --- /dev/null +++ b/es/components/mobile/me/index.d.ts @@ -0,0 +1,5 @@ +/// +declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => import("react").ReactElement>; +export default _default; diff --git a/es/components/mobile/me/index.js b/es/components/mobile/me/index.js new file mode 100644 index 000000000..fc2e5cfce --- /dev/null +++ b/es/components/mobile/me/index.js @@ -0,0 +1,96 @@ +export default OakComponent({ + entity: 'mobile', + isList: true, + projection: { + id: 1, + mobile: 1, + userId: 1, + ableState: 1, + }, + filters: [ + { + filter() { + // const token = this.features.token.getToken(); + const userId = this.features.token.getUserId(); + return { + userId, + }; + }, + }, + ], + formData: ({ data: mobiles, features }) => { + const token = features.token.getToken(); + const tokenMobileId = token.entity === 'mobile' && token.entityId; + return { + tokenMobileId, + mobiles, + allowRemove: mobiles.length > 1, + }; + }, + data: { + confirmDeleteModalVisible: false, + refreshing: false, + deleteIdx: undefined, + }, + properties: { + showBack: false, + }, + methods: { + async onRefreshMobile(e) { + this.setState({ + refreshing: true, + }); + try { + const { code, errMsg } = e.detail; + if (errMsg !== 'getPhoneNumber:ok') { + console.error(errMsg); + this.setMessage({ + title: '获取手机号失败', + type: 'warning', + }); + } + else { + await this.features.token.getWechatMpUserPhoneNumber(code); + } + } + catch (err) { + console.error(err); + } + this.setState({ + refreshing: false, + }); + }, + goAddMobile() { + const eventLoggedIn = `mobile:me:login:${Date.now()}`; + this.subEvent(eventLoggedIn, () => { + this.navigateBack(); + }); + this.navigateTo({ + url: '/mobile/login', + eventLoggedIn, + }); + }, + async onRemoveConfirm() { + const { mobileId } = this.state; + this.removeItem(mobileId); + await this.execute(); + this.setState({ + confirmDeleteModalVisible: false, + mobileId: '', + }); + }, + onRemoveModalOpen(e) { + const mobileId = e.currentTarget.dataset.id; + this.setState({ + confirmDeleteModalVisible: true, + mobileId, + }); + }, + onRemoveModalClose() { + this.setState({ + confirmDeleteModalVisible: false, + mobileId: '', + }); + }, + }, +}); diff --git a/src/pages/mobile/me/index.json b/es/components/mobile/me/index.json similarity index 100% rename from src/pages/mobile/me/index.json rename to es/components/mobile/me/index.json diff --git a/src/pages/mobile/me/index.less b/es/components/mobile/me/index.less similarity index 100% rename from src/pages/mobile/me/index.less rename to es/components/mobile/me/index.less diff --git a/src/pages/mobile/me/index.xml b/es/components/mobile/me/index.xml similarity index 100% rename from src/pages/mobile/me/index.xml rename to es/components/mobile/me/index.xml diff --git a/src/pages/mobile/me/mobile.module.less b/es/components/mobile/me/mobile.module.less similarity index 100% rename from src/pages/mobile/me/mobile.module.less rename to es/components/mobile/me/mobile.module.less diff --git a/es/components/mobile/me/web.d.ts b/es/components/mobile/me/web.d.ts new file mode 100644 index 000000000..7b100c324 --- /dev/null +++ b/es/components/mobile/me/web.d.ts @@ -0,0 +1,9 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function render(props: WebComponentProps void; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/mobile/me/web.js b/es/components/mobile/me/web.js new file mode 100644 index 000000000..6df0ffc93 --- /dev/null +++ b/es/components/mobile/me/web.js @@ -0,0 +1,23 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +import { List, Button, Dialog } from 'antd-mobile'; +import { MobileOutlined, DeleteOutlined } from '@ant-design/icons'; +import Style from './mobile.module.less'; +export default function render(props) { + const { mobiles, allowRemove, tokenMobileId } = props.data; + const { goAddMobile, removeItem, recoverItem, execute } = props.methods; + return (_jsxs("div", { className: Style.container, children: [mobiles && mobiles.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(List, { className: Style.list, children: mobiles?.map((ele, index) => (_jsx(List.Item, { prefix: _jsx(MobileOutlined, {}), extra: allowRemove && tokenMobileId !== ele.id && (_jsx("div", { onClick: async () => { + const result = await Dialog.confirm({ + content: '确认删除吗?删除后无法用此号码登录', + }); + if (result) { + removeItem(ele.id); + try { + await execute(); + } + catch (err) { + recoverItem(ele.id); + throw err; + } + } + }, children: _jsx(DeleteOutlined, {}) })), children: ele.mobile }, index))) }), _jsx("div", { style: { flex: 1 } })] })) : (_jsx("div", { className: Style.noData, children: _jsx("span", { children: "\u5C1A\u672A\u7ED1\u5B9A\u624B\u673A\u53F7" }) })), _jsx(Button, { block: true, size: "large", color: "primary", onClick: () => goAddMobile(), children: "\u7ED1\u5B9A" })] })); +} diff --git a/es/components/mobile/me/web.pc.d.ts b/es/components/mobile/me/web.pc.d.ts new file mode 100644 index 000000000..f91e4f872 --- /dev/null +++ b/es/components/mobile/me/web.pc.d.ts @@ -0,0 +1,10 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function render(props: WebComponentProps void; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/mobile/me/web.pc.js b/es/components/mobile/me/web.pc.js new file mode 100644 index 000000000..3391c1bbc --- /dev/null +++ b/es/components/mobile/me/web.pc.js @@ -0,0 +1,39 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState } from 'react'; +import { List, Button, Modal, Row, Col } from 'antd'; +import { MobileOutlined, DeleteOutlined } from '@ant-design/icons'; +import MobileLogin from '../login'; +export default function render(props) { + const { mobiles, allowRemove, tokenMobileId, showBack = false } = props.data; + const { goAddMobile, removeItem, recoverItem, execute, subEvent } = props.methods; + const [open, setOpen] = useState(false); + const eventLoggedIn = `user:info:login:${Date.now()}`; + return (_jsxs(_Fragment, { children: [_jsx(Button, { type: "primary", onClick: () => { + setOpen(true); + }, style: { marginBottom: 16 }, children: "\u7ED1\u5B9A" }), _jsx(Row, { children: _jsx(Col, { xs: 24, sm: 12, children: _jsx(List, { bordered: true, children: mobiles?.map((ele, index) => (_jsx(List.Item, { extra: allowRemove && + tokenMobileId !== ele.id && (_jsx("div", { onClick: () => { + const modal = Modal.confirm({ + title: `确认删除吗?删除后无法用此号码登录`, + okText: '确定', + cancelText: '取消', + onOk: async (e) => { + removeItem(ele.id); + try { + await execute(); + } + catch (err) { + recoverItem(ele.id); + throw err; + } + modal.destroy(); + }, + onCancel: (e) => { + modal.destroy(); + }, + }); + }, children: _jsx(DeleteOutlined, {}) })), children: _jsx(List.Item.Meta, { avatar: _jsx(MobileOutlined, {}), title: ele.mobile }) }, index))) }) }) }), _jsx(Modal, { title: "\u7ED1\u5B9A\u624B\u673A\u53F7", open: open, destroyOnClose: true, footer: null, onCancel: () => { + setOpen(false); + }, children: _jsx("div", { style: { padding: 16 }, children: _jsx(MobileLogin, { callback: () => { + setOpen(false); + }, oakPath: "$mobile/me-mobile/login", oakAutoUnmount: true }) }) })] })); +} diff --git a/es/components/token/me/index.d.ts b/es/components/token/me/index.d.ts new file mode 100644 index 000000000..d8903f829 --- /dev/null +++ b/es/components/token/me/index.d.ts @@ -0,0 +1,4 @@ +/// +/// +declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => import("react").ReactElement>; +export default _default; diff --git a/es/components/token/me/index.js b/es/components/token/me/index.js new file mode 100644 index 000000000..4ac44581b --- /dev/null +++ b/es/components/token/me/index.js @@ -0,0 +1,154 @@ +export default OakComponent({ + entity: 'token', + isList: true, + projection: { + id: 1, + userId: 1, + playerId: 1, + user: { + id: 1, + nickname: 1, + name: 1, + birth: 1, + gender: 1, + idState: 1, + userState: 1, + isRoot: 1, + extraFile$entity: { + $entity: 'extraFile', + data: { + id: 1, + tag1: 1, + origin: 1, + bucket: 1, + objectId: 1, + filename: 1, + extra1: 1, + type: 1, + entity: 1, + entityId: 1, + extension: 1, + }, + filter: { + tag1: 'avatar', + }, + indexFrom: 0, + count: 1, + }, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + }, + }, + }, + player: { + id: 1, + isRoot: 1, + }, + }, + filters: [ + { + filter() { + const tokenId = this.features.token.getTokenValue(); + if (tokenId) { + return { + id: tokenId, + }; + } + return { + id: 'none', + }; + }, + }, + ], + formData: ({ data, features }) => { + const [token] = data || []; + const user = token?.user; + const player = token?.player; + const avatarFile = user?.extraFile$entity && user?.extraFile$entity[0]; + const avatar = features.extraFile.getUrl(avatarFile); + const nickname = user && user.nickname; + const mobileData = user && user.mobile$user && user.mobile$user[0]; + const { mobile } = mobileData || {}; + const mobileCount = user?.mobile$user?.length || 0; + const isLoggedIn = !!token; + const isPlayingAnother = token && token.userId !== token.playerId; + const isRoot = !!player?.isRoot; + const mobileText = mobileCount && mobileCount > 1 + ? `${mobileCount}条手机号` + : mobile || '未绑定'; + return { + tokenId: token?.id, + userId: user?.id, + avatar, + nickname, + mobile, + mobileCount, + mobileText, + isLoggedIn, + isPlayingAnother, + isRoot, + }; + }, + data: { + refreshing: false, + }, + methods: { + async doLogin() { + this.setState({ + refreshing: true, + }); + try { + switch (process.env.OAK_PLATFORM) { + case 'wechatMp': { + await this.features.token.loginWechatMp(); + this.setState({ + refreshing: false, + }); + break; + } + case 'web': { + const eventLoggedIn = `token:me:login:${Date.now()}`; + this.subEvent(eventLoggedIn, () => { + this.navigateBack(); + }); + this.navigateTo({ + url: '/login', + eventLoggedIn, + }, undefined, true); + break; + } + } + } + catch (err) { + console.error(err); + } + }, + goMyMobile() { + this.navigateTo({ + url: '/mobile/me', + }); + }, + goUserManage() { + this.navigateTo({ + url: '/user/manage', + }); + }, + goSetting() { + this.navigateTo({ + url: '/setting', + }); + }, + goMyInfo() { + if (!this.state.isLoggedIn) { + return; + } + this.navigateTo({ + url: '/user/info', + oakId: this.state.userId, + }); + }, + }, +}); diff --git a/src/pages/token/me/index.json b/es/components/token/me/index.json similarity index 100% rename from src/pages/token/me/index.json rename to es/components/token/me/index.json diff --git a/src/pages/token/me/index.less b/es/components/token/me/index.less similarity index 100% rename from src/pages/token/me/index.less rename to es/components/token/me/index.less diff --git a/src/pages/token/me/index.xml b/es/components/token/me/index.xml similarity index 100% rename from src/pages/token/me/index.xml rename to es/components/token/me/index.xml diff --git a/src/pages/token/me/locales/zh_CN.json b/es/components/token/me/locales/zh_CN.json similarity index 100% rename from src/pages/token/me/locales/zh_CN.json rename to es/components/token/me/locales/zh_CN.json diff --git a/src/pages/token/me/mobile.module.less b/es/components/token/me/mobile.module.less similarity index 100% rename from src/pages/token/me/mobile.module.less rename to es/components/token/me/mobile.module.less diff --git a/es/components/token/me/web.d.ts b/es/components/token/me/web.d.ts new file mode 100644 index 000000000..7c23209aa --- /dev/null +++ b/es/components/token/me/web.d.ts @@ -0,0 +1,18 @@ +import { EntityDict } from '../../../oak-app-domain'; +import { WebComponentProps } from 'oak-frontend-base'; +export default function Render(props: WebComponentProps Promise; + doLogin: () => Promise; + goMyMobile: () => Promise; + goUserManage: () => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/token/me/web.js b/es/components/token/me/web.js new file mode 100644 index 000000000..f718da233 --- /dev/null +++ b/es/components/token/me/web.js @@ -0,0 +1,9 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { List, Button, Avatar } from 'antd-mobile'; +import { UserOutlined, MobileOutlined } from '@ant-design/icons'; +import Style from './mobile.module.less'; +export default function Render(props) { + const { avatar, isLoggedIn, refreshing, mobileText, isRoot, oakExecuting, tokenId, nickname, oakDirty, } = props.data; + const { doLogin, t, goMyMobile, goUserManage, goMyInfo } = props.methods; + return (_jsxs("div", { className: Style.container, children: [_jsxs("div", { className: Style.userInfo, children: [_jsx(Avatar, { className: Style.avatar, src: avatar }), _jsx("span", { className: Style.nickname, children: nickname || '未设置' }), isLoggedIn ? (_jsx(Button, { color: "primary", size: "small", disabled: refreshing, loading: refreshing, onClick: () => goMyInfo(), children: t('common::action.update') })) : (_jsx(Button, { size: "small", disabled: refreshing, loading: refreshing, onClick: () => doLogin(), children: t('login') }))] }), _jsxs(List, { className: Style.list, children: [_jsx(List.Item, { onClick: () => goMyMobile(), prefix: _jsx(MobileOutlined, {}), title: "\u624B\u673A\u53F7", extra: mobileText }), isRoot && (_jsx(List.Item, { onClick: () => goUserManage(), prefix: _jsx(UserOutlined, {}), title: "\u7528\u6237\u7BA1\u7406" }))] })] })); +} diff --git a/src/pages/token/me/web.module.less b/es/components/token/me/web.module.less similarity index 100% rename from src/pages/token/me/web.module.less rename to es/components/token/me/web.module.less diff --git a/es/components/token/me/web.pc.d.ts b/es/components/token/me/web.pc.d.ts new file mode 100644 index 000000000..f8c59b723 --- /dev/null +++ b/es/components/token/me/web.pc.d.ts @@ -0,0 +1,18 @@ +import { EntityDict } from '../../../oak-app-domain'; +import { WebComponentProps } from 'oak-frontend-base'; +export default function Render(props: WebComponentProps Promise; + doLogin: () => Promise; + goMyMobile: () => Promise; + goUserManage: () => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/token/me/web.pc.js b/es/components/token/me/web.pc.js new file mode 100644 index 000000000..9622b2b4c --- /dev/null +++ b/es/components/token/me/web.pc.js @@ -0,0 +1,9 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { List, Button, Avatar } from 'antd'; +import { UserOutlined, MobileOutlined } from '@ant-design/icons'; +import Style from './web.module.less'; +export default function Render(props) { + const { avatar, isLoggedIn, refreshing, mobileText, isRoot, oakExecuting, tokenId, nickname, oakDirty, } = props.data; + const { doLogin, t, goMyMobile, goUserManage, goMyInfo } = props.methods; + return (_jsxs("div", { className: Style.container, children: [_jsxs("div", { className: Style.userInfo, children: [avatar ? (_jsx(Avatar, { className: Style.avatar, src: avatar })) : (_jsx(Avatar, { className: Style.avatar, icon: _jsx(UserOutlined, { className: Style.userIcon }) })), _jsx("span", { className: Style.nickname, children: nickname || '未设置' }), isLoggedIn ? (_jsx(Button, { type: "primary", size: "small", disabled: refreshing, loading: refreshing, onClick: () => goMyInfo(), children: t('common::action.update') })) : (_jsx(Button, { size: "small", disabled: refreshing, loading: refreshing, onClick: () => doLogin(), children: t('login') }))] }), _jsxs(List, { className: Style.list, split: true, children: [_jsx(List.Item, { onClick: () => goMyMobile(), children: _jsx(List.Item.Meta, { avatar: _jsx(MobileOutlined, {}), title: "\u624B\u673A\u53F7", description: mobileText }) }), isRoot && (_jsx(List.Item, { onClick: () => goUserManage(), children: _jsx(List.Item.Meta, { avatar: _jsx(UserOutlined, {}), title: "\u7528\u6237\u7BA1\u7406" }) }))] })] })); +} diff --git a/es/components/user/info/index.d.ts b/es/components/user/info/index.d.ts new file mode 100644 index 000000000..e9a9dadcf --- /dev/null +++ b/es/components/user/info/index.d.ts @@ -0,0 +1,4 @@ +/// +/// +declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => import("react").ReactElement>; +export default _default; diff --git a/es/components/user/info/index.js b/es/components/user/info/index.js new file mode 100644 index 000000000..b833faa4c --- /dev/null +++ b/es/components/user/info/index.js @@ -0,0 +1,348 @@ +import { OakUserInvisibleException } from 'oak-domain/lib/types'; +import dayjs from 'dayjs'; +import { LOCAL_STORAGE_KEYS } from '../../../config/constants'; +const SEND_KEY = LOCAL_STORAGE_KEYS.captchaSendAt; +const SEND_CAPTCHA_LATENCY = process.env.NODE_ENV === 'development' ? 10 : 60; +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + name: 1, + nickname: 1, + birth: 1, + gender: 1, + idState: 1, + userState: 1, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + userId: 1, + user: { + id: 1, + userState: 1, + refId: 1, + }, + }, + }, + user$ref: { + $entity: 'user', + data: { + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + userId: 1, + user: { + id: 1, + userState: 1, + refId: 1, + }, + }, + }, + }, + }, + extraFile$entity: { + $entity: 'extraFile', + data: { + id: 1, + tag1: 1, + origin: 1, + bucket: 1, + objectId: 1, + filename: 1, + extra1: 1, + extension: 1, + type: 1, + entity: 1, + entityId: 1, + }, + filter: { + tag1: 'avatar', + }, + indexFrom: 0, + count: 1, + }, + wechatUser$user: { + $entity: 'wechatUser', + data: { + id: 1, + openId: 1, + unionId: 1, + userId: 1, + origin: 1, + nickname: 1, + user: { + id: 1, + userState: 1, + refId: 1, + }, + }, + }, + }, + isList: false, + formData({ data: user, features }) { + const avatar = user?.extraFile$entity && user?.extraFile$entity[0]; + const avatarUrl = features.extraFile.getUrl(avatar); + const { mobile } = (user?.mobile$user && user?.mobile$user[0]) || + (user?.user$ref && + user?.user$ref[0] && + user?.user$ref[0].mobile$user && + user?.user$ref[0].mobile$user[0]) || + {}; + const genderOption = user?.gender && + this.state.genderOptions.find((ele) => ele.value === user?.gender); + const lastSendAt = features.localStorage.load(SEND_KEY); + const now = Date.now(); + let counter = 0; + if (typeof lastSendAt === 'number') { + counter = Math.max(SEND_CAPTCHA_LATENCY - Math.ceil((now - lastSendAt) / 1000), 0); + if (counter > 0) { + this.counterHandler = setTimeout(() => this.reRender(), 1000); + } + else if (this.counterHandler) { + clearTimeout(this.counterHandler); + this.counterHandler = undefined; + } + } + const isRoot = features.token.isReallyRoot(); + return { + id: user?.id, + name: user?.name, + nickname: user?.nickname, + gender: user?.gender, + genderStr: genderOption?.label, + birthText: user?.birth + ? dayjs(user.birth).format('YYYY-MM-DD') + : '', + birth: user?.birth, + avatarUrl, + mobile, + userState: user?.userState, + idState: user?.idState, + wechatUser: user?.wechatUser$user?.[0], + counter, + isRoot, + }; + }, + data: { + stateColor: { + shadow: 'primary', + normal: 'success', + disabled: 'danger', + }, + idStateColor: { + verifying: 'primary', + verified: 'success', + unverified: 'warning', + }, + genderOptions: [ + { + value: 'male', + label: '男', + }, + { + value: 'female', + label: '女', + }, + ], + visible: false, + attr: '', + attrs: { + nickname: '昵称', + gender: '性别', + birth: '出生日期', + }, + birthEnd: '', + refreshing: false, + }, + lifetimes: { + async ready() { + const { oakId } = this.props; + const userId = this.features.token.getUserId(); + if (userId !== oakId) { + throw new OakUserInvisibleException(); + } + this.setState({ birthEnd: dayjs().format('YYYY-MM-DD') }); + }, + }, + methods: { + async refreshWechatPublicUserInfo() { + this.setState({ + refreshing: true, + }); + try { + await this.features.token.refreshWechatPublicUserInfo(); + this.setState({ + refreshing: false, + }); + } + catch (err) { + this.setState({ + refreshing: false, + }); + throw err; + } + }, + goUserManage() { + this.navigateTo({ + url: '/user/manage', + }); + }, + goAddMobile() { + this.navigateTo({ + url: '/mobile/me', + }, { + showBack: true, + }); + }, + goChangePassword() { + this.navigateTo({ + url: '/changePassword', + }, { + showBack: true, + }); + }, + setVisibleMp(e) { + const { target: { dataset }, } = e; + const { attr } = dataset; + this.setVisible(true, attr); + }, + genderChangeMp(e) { + const { detail } = e; + const { checked, currentKey } = detail; + const { attr } = this.state; + this.setCustomData(attr, currentKey); + }, + birthChangeMp(e) { + const { detail: { value }, } = e; + const birth = dayjs(dayjs(value).format('YYYY-MM-DD')).valueOf(); + const { attr } = this.state; + this.setState({ + birthText2: dayjs(value).format('YYYY-MM-DD'), + }); + this.setCustomData(attr, birth); + }, + setVisible(visible, attr) { + this.setState({ + visible, + attr: visible ? attr : '', + [`new_${attr}`]: '', + birthText2: '', + }); + }, + setCustomData(attr, value) { + this.setState({ + [`new_${attr}`]: value, + }); + }, + setCustomDataMp(e) { + const { detail, target: { dataset }, } = e; + const { value } = detail; + const { attr } = this.state; + this.setCustomData(attr, value); + }, + updateData(attr, value) { + this.update({ + [attr]: this.state[`new_${attr}`], + }); + }, + async onConfirmMp() { + const { attr } = this.state; + await this.onConfirm(attr); + }, + async onConfirm(attr) { + const { oakId } = this.props; + if (!this.state[`new_${attr}`]) { + this.setMessage({ + type: 'warning', + content: `${this.state.attrs[attr]}不能为空`, + }); + return; + } + this.update({ + [attr]: this.state[`new_${attr}`], + }); + await this.execute(); + this.setVisible(false, attr); + }, + onPupopCloseMp() { + const { attr } = this.state; + this.clean(); + this.setVisible(false, attr); + }, + async updateMyInfo() { + if (!this.state.name) { + this.setMessage({ + type: 'error', + content: '请输入姓名', + }); + return; + } + if (!this.state.nickname) { + this.setMessage({ + type: 'error', + content: '请输入昵称', + }); + return; + } + // if (!this.state.gender) { + // this.setMessage({ + // type: 'error', + // content: '请选择性别', + // }); + // return; + // } + // if (!this.state.birth) { + // this.setMessage({ + // type: 'error', + // content: '请选择生日', + // }); + // return; + // } + await this.execute('update'); + }, + async sendCaptcha() { + const { mobile } = this.state; + try { + const result = await this.features.token.sendCaptcha(mobile, 'login'); + // 显示返回消息 + this.setMessage({ + type: 'success', + content: result, + }); + this.save(SEND_KEY, Date.now()); + this.reRender(); + } + catch (err) { + this.setMessage({ + type: 'error', + content: err.message, + }); + } + }, + async unbindingWechat(captcha) { + const { mobile, wechatUser } = this.state; + try { + await this.features.cache.exec('unbindingWechat', { + wechatUserId: wechatUser.id, + mobile, + captcha, + }); + this.refresh(); + this.setMessage({ + content: '解绑成功', + type: 'success', + }); + } + catch (err) { + this.setMessage({ + content: '解绑失败', + type: 'warning', + }); + } + }, + }, +}); diff --git a/es/components/user/info/index.json b/es/components/user/info/index.json new file mode 100644 index 000000000..7b1beae1c --- /dev/null +++ b/es/components/user/info/index.json @@ -0,0 +1,17 @@ +{ + "navigationBarTitleText": "个人设置", + "enablePullDownRefresh": false, + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-tag": "@oak-frontend-base/miniprogram_npm/lin-ui/tag/index", + "l-popup": "@oak-frontend-base/miniprogram_npm/lin-ui/popup/index", + "l-input": "@oak-frontend-base/miniprogram_npm/lin-ui/input/index", + "l-radio-group": "@oak-frontend-base/miniprogram_npm/lin-ui/radio-group/index", + "l-radio": "@oak-frontend-base/miniprogram_npm/lin-ui/radio/index", + "l-form-item": "@oak-frontend-base/miniprogram_npm/lin-ui/form-item/index", + "oak-extraFile-avatar": "../../../components/extraFile/avatar/index" + } +} diff --git a/es/components/user/info/index.less b/es/components/user/info/index.less new file mode 100644 index 000000000..cfa36719d --- /dev/null +++ b/es/components/user/info/index.less @@ -0,0 +1,90 @@ +/** index.wxss **/ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.avatar { + margin: 10rpx 0; +} + + +.col { + margin-top: 20rpx; + display: flex; + flex-direction: column; + padding: 0rpx 10rpx; + background-color: #fff; + + .left { + display: flex; + flex-direction: row; + } + + .icon { + width: 16rpx; + margin-left: 6rpx; + margin-right: 26rpx; + text-align: center; + } + + .label { + color: @oak-text-color-primary; + } + + .value { + color: @oak-text-color-secondary; + margin-right: 16rpx; + + .primary { + background-color: @oak-color-primary; + } + + .success { + background-color: @oak-color-success; + } + + .danger { + background-color: @oak-color-error; + } + + .warning { + background-color: @oak-color-warning; + } + } +} + + +.pupop-content { + display: flex; + flex-direction: column; + background-color: #fff; + min-height: 300rpx; + .safe-area-inset-bottom(); + + .pupop-header { + padding: 20rpx; + display: flex; + flex-direction: column; + align-items: flex-end; + + + .close { + display: block; + } + } + + .pupop-form { + display: flex; + flex-direction: column; + flex: 1; + } +} \ No newline at end of file diff --git a/es/components/user/info/index.xml b/es/components/user/info/index.xml new file mode 100644 index 000000000..a824eb1cc --- /dev/null +++ b/es/components/user/info/index.xml @@ -0,0 +1,82 @@ + + + + + + + + + {{nickname || '未设置'}} + + + {{name || '未设置'}} + + + {{genderStr || '未设置'}} + + + {{birthText || '未设置'}} + + + {{mobile || '未绑定'}} + + + + + + {{t('user:v.idState.' + idState) || '未设置'}} + + + + + + + + + + + + + + + + + + + + + + + + + {{item.label}} + + + + + + + + + + + + + + + + + + + + 提交 + + + + \ No newline at end of file diff --git a/es/components/user/info/locales/zh_CN.json b/es/components/user/info/locales/zh_CN.json new file mode 100644 index 000000000..9efe395d2 --- /dev/null +++ b/es/components/user/info/locales/zh_CN.json @@ -0,0 +1,16 @@ +{ + "avatar": "头像", + "mobile": "手机号", + "password": "密码", + "manage": "管理", + "bind": "绑定", + "syncWeChat": "同步微信信息", + "send": "发送验证码", + "cancel": "取消", + "unbind": "解绑", + "Mobile-Number-Verification": "手机号验证", + "unbindingWechat": "确定解绑微信账号", + "placeholder": { + "Captcha": "输入4位短信验证码" + } +} diff --git a/es/components/user/info/mobile.module.less b/es/components/user/info/mobile.module.less new file mode 100644 index 000000000..17a0a95d6 --- /dev/null +++ b/es/components/user/info/mobile.module.less @@ -0,0 +1,38 @@ +.container { + background: var(--oak-bg-color-page); + display: flex; + flex-direction: column; + flex: 1; + height: 100vh; +} + +.syncWeChat { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin-top: 10px; +} + +.avatar_container { + height: 220px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + .avatar { + width: 84px; + height: 84px; + border-radius: 42px; + margin-bottom: 10px; + } +} + + +.list { + flex: 1; + margin-top: 10px; + margin-bottom: 10px; + background: var(--oak-bg-color-container); +} diff --git a/es/components/user/info/web.d.ts b/es/components/user/info/web.d.ts new file mode 100644 index 000000000..328473f8c --- /dev/null +++ b/es/components/user/info/web.d.ts @@ -0,0 +1,32 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +type DataProps = { + visible: boolean; + nickname: string; + name: string; + birth: string; + gender: string; + mobile: string; + avatarUrl: string; + attr: string; + genderOptions: Array<{ + label: string; + value: string; + }>; + attrs: Record; + id: string; + refreshing: boolean; + isSupportSyncWeChat: boolean; + appId: string; +}; +type MethodsProps = { + goAddMobile: () => void; + goChangePassword: () => void; + setAvatar: () => void; + setVisible: (visible: boolean, attr: string) => void; + setCustomData: (attr: string, value: string | number) => void; + onConfirm: (attr: string) => Promise; + refreshWechatPublicUserInfo: () => void; +}; +export default function render(props: WebComponentProps): import("react/jsx-runtime").JSX.Element; +export {}; diff --git a/es/components/user/info/web.js b/es/components/user/info/web.js new file mode 100644 index 000000000..01971c652 --- /dev/null +++ b/es/components/user/info/web.js @@ -0,0 +1,49 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { List, DatePicker, Popup, Form, Button, Input, Radio, Space, } from 'antd-mobile'; +import dayjs from 'dayjs'; +import OakAvatar from '../../../components/extraFile/avatar'; +import Style from './mobile.module.less'; +export default function render(props) { + const { data, methods } = props; + const { t, clean, setAvatar, setVisible, goAddMobile, refreshWechatPublicUserInfo, goChangePassword, } = methods; + const { oakFullpath, visible, nickname, name, birth, gender, mobile, avatarUrl, attr, id, isSupportSyncWeChat, refreshing, } = data; + return (_jsxs("div", { className: Style.container, children: [_jsxs(List, { className: Style.list, children: [_jsx(List.Item, { extra: _jsx("div", { style: { marginTop: 5, marginBottom: 5 }, children: _jsx(OakAvatar, { oakAutoUnmount: true, oakPath: oakFullpath + ? oakFullpath + '.extraFile$entity' + : undefined, entity: "user", entityId: id, autoUpload: true }) }), children: "\u5934\u50CF" }), _jsx(List.Item, { extra: nickname ? nickname : '未设置', onClick: () => { + setVisible(true, 'nickname'); + }, children: t('user:attr.nickname') }), _jsx(List.Item, { extra: gender ? t(`user:v.gender.${gender}`) : '未设置', onClick: () => { + setVisible(true, 'gender'); + }, children: t('user:attr.gender') }), _jsx(List.Item, { extra: birth ? dayjs(birth).format('YYYY-MM-DD') : '未设置', onClick: () => { + setVisible(true, 'birth'); + }, children: t('user:attr.birth') }), _jsx(List.Item, { extra: mobile ? mobile : '未设置', onClick: () => { + goAddMobile(); + }, children: t('mobile') }), _jsx(List.Item, { extra: '********', onClick: () => { + goChangePassword(); + }, children: t('password') })] }), _jsx(Popup, { visible: visible, onMaskClick: () => { + clean(); + setVisible(false, attr); + }, bodyStyle: { + borderTopLeftRadius: '8px', + borderTopRightRadius: '8px', + minHeight: '20vh', + }, children: _jsx(AttrUpsert, { data: data, methods: methods }) })] })); +} +function AttrUpsert(props) { + const { data, methods } = props; + const { attr, genderOptions, attrs } = data; + const { setCustomData, onConfirm, setVisible } = methods; + const label = attrs[attr]; + return (_jsx("div", { children: _jsxs(Form, { footer: _jsx(Button, { block: true, type: "submit", color: "primary", size: "large", onClick: async () => { + await onConfirm(attr); + }, children: "\u63D0\u4EA4" }), children: [_jsx(Form.Header, { children: "\u4FEE\u6539\u4FE1\u606F" }), attr === 'nickname' && (_jsx(Form.Item, { name: attr, label: label, rules: [{ required: true }], children: _jsx(Input, { placeholder: `请输入${label}`, defaultValue: data[attr], onChange: (value) => { + setCustomData(attr, value); + } }) })), attr === 'gender' && (_jsx(Form.Item, { name: attr, label: label, rules: [{ required: true }], children: _jsx(Radio.Group, { defaultValue: data[attr], onChange: (value) => { + setCustomData(attr, value); + }, children: _jsx(Space, { direction: "vertical", children: genderOptions.map((ele) => (_jsx(Radio, { value: ele.value, children: ele.label }))) }) }) })), attr === 'birth' && (_jsx(Form.Item, { name: attr, label: label, onClick: (e, datePickerRef) => { + datePickerRef.current?.open(); + }, children: _jsx(DatePicker, { defaultValue: data[attr] ? dayjs(data[attr]).toDate() : null, onConfirm: (value) => { + setCustomData(attr, dayjs(value).startOf('day').valueOf()); + }, max: dayjs().toDate(), children: (value) => value + ? dayjs(value).format('YYYY-MM-DD') + : '请选择日期' }) }))] }) })); +} diff --git a/es/components/user/info/web.module.less b/es/components/user/info/web.module.less new file mode 100644 index 000000000..c9848913a --- /dev/null +++ b/es/components/user/info/web.module.less @@ -0,0 +1,7 @@ + +.container { + background: var(--oak-bg-color-container); + box-shadow: 0 2px 3px #0000001a; + border-radius: 3px; + padding: 30px 32px; +} diff --git a/es/components/user/info/web.pc.d.ts b/es/components/user/info/web.pc.d.ts new file mode 100644 index 000000000..1ffeee850 --- /dev/null +++ b/es/components/user/info/web.pc.d.ts @@ -0,0 +1,27 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function Render(props: WebComponentProps; + wechatUser: EntityDict['wechatUser']['Schema']; + counter: number; + isRoot: boolean; +}, { + goUserManage: () => void; + goAddMobile: () => void; + sendCaptcha: () => void; + goChangePassword: () => void; + updateMyInfo: () => void; + unbindingWechat: (captcha?: string) => void; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/info/web.pc.js b/es/components/user/info/web.pc.js new file mode 100644 index 000000000..bf80a7974 --- /dev/null +++ b/es/components/user/info/web.pc.js @@ -0,0 +1,89 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState } from 'react'; +import { Space, Button, Input, Radio, DatePicker, Form, Typography, Modal, Descriptions } from 'antd'; +import dayjs from 'dayjs'; +import PageHeader from '../../../components/common/pageHeader'; +import OakAvatar from '../../../components/extraFile/avatar'; +import ExtraFileCommit from '../../../components/extraFile/commit'; +import MobileLogin from '../../../components/mobile/login'; +import WechatLoginQrCode from '../../../components/wechatLogin/qrCode'; +import WechatUserList from '../../../components/wechatUser/bindingList'; +import { isCaptcha, } from 'oak-domain/lib/utils/validator'; +import Style from './web.module.less'; +export default function Render(props) { + const { data, methods } = props; + const { t, updateMyInfo, goAddMobile, sendCaptcha, unbindingWechat, goChangePassword, goUserManage, } = methods; + const { nickname, name, birth, gender, mobile, avatarUrl, showBack, oakExecuting, genderOptions, oakFullpath, oakDirty, wechatUser, counter, isRoot, } = data; + const [open, setOpen] = useState(false); + const [open2, setOpen2] = useState(false); + const [open3, setOpen3] = useState(false); + const [captcha, setCaptcha] = useState(''); + return (_jsxs(PageHeader, { title: "\u4E2A\u4EBA\u4FE1\u606F", showBack: showBack, children: [_jsxs("div", { className: Style.container, children: [_jsx(Descriptions, { title: '基本信息' }), _jsxs(Form, { labelCol: { xs: { span: 4 }, md: { span: 6 } }, wrapperCol: { xs: { span: 16 }, md: { span: 12 } }, children: [_jsx(Form.Item, { label: t('avatar'), name: "extraFile$entity", children: _jsx(_Fragment, { children: _jsx(OakAvatar, { oakAutoUnmount: true, oakPath: oakFullpath + ? oakFullpath + '.extraFile$entity' + : undefined, entity: "user" }) }) }), _jsx(Form.Item, { label: t('user:attr.name'), rules: [ + { + required: true, + }, + ], children: _jsx(_Fragment, { children: _jsx(Input, { placeholder: "", onChange: (e) => methods.update({ + name: e.target.value, + }), value: name }) }) }), _jsx(Form.Item, { label: t('user:attr.nickname'), rules: [ + { + required: true, + }, + ], children: _jsx(_Fragment, { children: _jsx(Input, { placeholder: "", onChange: (e) => methods.update({ + nickname: e.target.value, + }), value: nickname }) }) }), _jsx(Form.Item, { label: t('user:attr.gender'), children: _jsx(Space, { direction: "vertical", children: _jsx(Radio.Group, { value: data.gender, options: genderOptions, onChange: ({ target: { value } }) => { + methods.update({ gender: value }); + } }) }) }), _jsx(Form.Item, { label: t('user:attr.birth'), children: _jsx(_Fragment, { children: _jsx(DatePicker, { placeholder: "\u8BF7\u9009\u62E9", format: "YYYY-MM-DD", inputReadOnly: true, allowClear: false, value: birth ? dayjs(birth) : undefined, disabledDate: (current) => { + if (dayjs(current).valueOf() > + dayjs().valueOf()) { + return true; + } + return false; + }, onChange: (value) => { + if (value) { + methods.update({ + birth: dayjs(value).valueOf(), + }); + } + } }) }) }), _jsx(Form.Item, { wrapperCol: { + xs: { offset: 4 }, + md: { offset: 6 }, + }, children: _jsx(Space, { children: _jsx(ExtraFileCommit, { oakPath: oakFullpath }) }) })] })] }), _jsx("div", { style: { marginTop: '10px' } }), _jsxs("div", { className: Style.container, children: [_jsx(Descriptions, { title: '安全信息' }), _jsxs(Form, { labelCol: { xs: { span: 4 }, md: { span: 6 } }, wrapperCol: { xs: { span: 16 }, md: { span: 12 } }, children: [_jsx(Form.Item, { label: t('mobile'), children: _jsxs(Space, { children: [_jsx(Typography, { children: mobile || '未设置' }), _jsx(Button, { size: "small", onClick: () => { + if (mobile) { + goAddMobile(); + return; + } + setOpen(true); + }, children: mobile ? t('manage') : t('bind') })] }) }), _jsx(Form.Item, { label: t('user:attr.password'), children: _jsxs(Space, { children: [_jsx(Typography, { children: '********' }), _jsx(Button, { size: "small", onClick: () => { + goChangePassword(); + return; + }, children: t('manage') })] }) }), process.env.NODE_ENV === 'development' && (_jsx(Form.Item, { label: "\u5FAE\u4FE1\u5E10\u53F7", children: _jsx(_Fragment, { children: wechatUser ? (_jsxs(Space, { children: [_jsx(Typography, { children: wechatUser.nickname }), _jsx(WechatUserList, { oakPath: oakFullpath + ? `${oakFullpath}.wechatUser$user` + : undefined })] })) : (_jsx(Button, { size: "small", onClick: () => { + setOpen2(true); + }, children: "\u7ED1\u5B9A" })) }) })), isRoot && (_jsx(Form.Item, { label: '系统用户', tooltip: "\u8D85\u7EA7\u7BA1\u7406\u5458\u53EF\u5BF9\u7CFB\u7EDF\u7528\u6237\u8FDB\u884C\u7BA1\u7406", children: _jsx(Button, { size: "small", onClick: () => { + goUserManage(); + }, children: t('manage') }) }))] })] }), _jsx(Modal, { title: "\u7ED1\u5B9A\u624B\u673A\u53F7", open: open, destroyOnClose: true, footer: null, onCancel: () => { + setOpen(false); + }, children: _jsx(MobileLogin, { callback: () => { + setOpen(false); + }, oakPath: "$user/info-mobile/login", oakAutoUnmount: true }) }), _jsx(Modal, { title: "\u7ED1\u5B9A\u5FAE\u4FE1", open: open2, destroyOnClose: true, footer: null, maskClosable: false, onCancel: () => { + setOpen2(false); + }, children: _jsx(WechatLoginQrCode, { oakPath: "$user/info-wechatLogin/qrCode", oakAutoUnmount: true }) }), _jsx(Modal, { title: t('Mobile-Number-Verification'), open: open3, destroyOnClose: true, footer: [ + _jsx(Button, { onClick: () => setOpen3(false), children: t('cancel') }, "cancel"), + _jsx(Button, { type: "primary", disabled: !isCaptcha(captcha), onClick: () => { + unbindingWechat(captcha); + setOpen3(false); + }, children: t('unbind') }, "send"), + ], maskClosable: false, onCancel: () => { + setOpen3(false); + }, children: _jsxs(Space, { direction: "vertical", style: { width: '100%' }, children: [_jsxs(Typography, { children: ["\u8BF7\u8F93\u5165", mobile && + mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'), "\u6536\u5230\u7684\u9A8C\u8BC1\u7801"] }), _jsx(Form.Item, { name: "captcha", children: _jsx(Input, { allowClear: true, value: captcha, "data-attr": "captcha", + // type="number" + maxLength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: (e) => { + setCaptcha(e.target.value); + }, className: Style['loginbox-input'], suffix: _jsx(Button, { type: "link", disabled: counter > 0, onClick: () => sendCaptcha(), children: counter > 0 + ? `${counter}秒后可重发` + : t('send') }) }) })] }) })] })); +} diff --git a/es/components/user/manage/detail/index.d.ts b/es/components/user/manage/detail/index.d.ts new file mode 100644 index 000000000..ec99a09a8 --- /dev/null +++ b/es/components/user/manage/detail/index.d.ts @@ -0,0 +1,4 @@ +/// +/// +declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => import("react").ReactElement>; +export default _default; diff --git a/es/components/user/manage/detail/index.js b/es/components/user/manage/detail/index.js new file mode 100644 index 000000000..8560a07c2 --- /dev/null +++ b/es/components/user/manage/detail/index.js @@ -0,0 +1,188 @@ +// index.ts +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + nickname: 1, + name: 1, + userState: 1, + birth: 1, + idState: 1, + gender: 1, + isRoot: 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, + }, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + }, + }, + ref: { + id: 1, + userState: 1, + }, + refId: 1, + }, + isList: false, + formData: ({ data: user, features }) => { + const { id, nickname, idState, userState, name, gender, mobile$user, birth, extraFile$entity, } = user || {}; + const legalActions = user?.['#oakLegalActions'] || []; + const mobile = mobile$user && mobile$user[0]?.mobile; + const mobileCount = mobile$user ? mobile$user.length : 0; + const mobileText = mobileCount && mobileCount > 1 + ? `${mobileCount}条手机号` + : mobile || '未设置'; + const avatar = features.extraFile.getUrl(extraFile$entity && extraFile$entity[0]); + const reallyRoot = features.token.isReallyRoot(); + const currentUserId = features.token.getUserId(); + const executableActions = reallyRoot && currentUserId !== id + ? legalActions.concat('play') + : legalActions; + return { + id, + nickname, + name, + mobile, + gender, + avatar, + birth: birth ? new Date(birth).toLocaleDateString() : '', + userState, + idState, + mobileCount, + mobileText, + executableActions, + }; + }, + actions: [ + 'accept', + 'activate', + 'disable', + 'enable', + 'remove', + 'update', + 'verify', + ], + data: { + stateColor: { + shadow: 'primary', + normal: 'success', + disabled: 'danger', + }, + idStateColor: { + verifying: 'primary', + verified: 'success', + unverified: 'warning', + }, + show: false, + actionDescriptions: { + accept: { + icon: { + name: 'success', + }, + label: '通过', + }, + activate: { + icon: { + name: 'playon', + }, + label: '激活', + }, + disable: { + icon: { + name: 'shielding', + }, + label: '禁用', + }, + enable: { + icon: { + name: 'barrage', + }, + label: '启用', + }, + remove: { + icon: { + name: 'trash', + }, + label: '删除', + }, + update: { + icon: { + name: 'editor', + }, + label: '更新', + }, + verify: { + icon: { + name: 'businesscard', + }, + label: '验证', + }, + play: { + icon: { + name: 'refresh', + }, + label: '切换', + }, + }, + genderOptions: { + male: '男', + female: '女', + }, + }, + methods: { + async onActionClick(action) { + switch (action) { + case 'update': { + this.navigateTo({ + url: '/user/manage/upsert', + oakId: this.props.oakId, + }); + return; + } + case 'enable': + case 'disable': + case 'accept': + case 'verify': + case 'activate': { + await this.execute(action); + break; + } + case 'play': { + const { id } = this.state; + await this.features.token.switchTo(id); + break; + } + default: { + console.error(`尚未实现的action: ${action}`); + } + } + if (action === 'play') { + this.navigateBack(2); + } + }, + onActionClickMp(e) { + const { action } = e.detail; + return this.onActionClick(action); + }, + }, +}); diff --git a/es/components/user/manage/detail/index.json b/es/components/user/manage/detail/index.json new file mode 100644 index 000000000..1c24a5cb4 --- /dev/null +++ b/es/components/user/manage/detail/index.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "用户详情", + "usingComponents": { + "actionPanel": "../../../../components/func/actionPanel/index", + "oak-icon": "../../../../components/icon/index", + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-tag": "@oak-frontend-base/miniprogram_npm/lin-ui/tag/index" + } +} diff --git a/es/components/user/manage/detail/index.less b/es/components/user/manage/detail/index.less new file mode 100644 index 000000000..d4eae92b5 --- /dev/null +++ b/es/components/user/manage/detail/index.less @@ -0,0 +1,84 @@ +/** index.wxss **/ +@import "../../../../config/styles/mp/index.less"; +@import "../../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + + +.avatar { + font-size: 128rpx; + color: @oak-text-color-secondary; + height: 140rpx; + width: 140rpx; + border-radius: 14rpx; +} + +.nickname { + font-size: 44rpx; + font-weight: bold; +} + +.col { + display: flex; + flex-direction: column; + padding: 0rpx 10rpx; + border-bottom: 1px solid #e0e0e0; + background-color: #fff; + + .left { + display: flex; + flex-direction: row; + } + .icon { + width: 16rpx; + margin-left: 6rpx; + margin-right: 26rpx; + text-align: center; + } + .label { + color: @oak-text-color-primary; + } + .value { + color: @oak-text-color-secondary; + margin-right: 16rpx; + + .primary { + background-color: @oak-color-primary; + } + + .success { + background-color: @oak-color-success; + } + + .danger { + background-color: @oak-color-error; + } + + .warning { + background-color: @oak-color-warning; + } + } +} + + +.userInfo { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 300rpx; +} + +.btn-view { + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/es/components/user/manage/detail/index.xml b/es/components/user/manage/detail/index.xml new file mode 100644 index 000000000..daa588306 --- /dev/null +++ b/es/components/user/manage/detail/index.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + {{nickname || '未设置'}} + + + {{name || '未设置'}} + + + {{genderOptions[gender] || '未设置'}} + + + {{birth || '未设置'}} + + + {{mobileText}} + + + + + {{userState || '未设置'}} + + + + + + + {{idState || '未设置'}} + + + + + + + \ No newline at end of file diff --git a/es/components/user/manage/detail/locales/zh_CN.json b/es/components/user/manage/detail/locales/zh_CN.json new file mode 100644 index 000000000..3b52cf392 --- /dev/null +++ b/es/components/user/manage/detail/locales/zh_CN.json @@ -0,0 +1,5 @@ +{ + "mobile": "手机号", + "unset": "未设置", + "avatar": "头像" +} diff --git a/es/components/user/manage/detail/mobile.module.less b/es/components/user/manage/detail/mobile.module.less new file mode 100644 index 000000000..9532034a6 --- /dev/null +++ b/es/components/user/manage/detail/mobile.module.less @@ -0,0 +1,13 @@ + + +.container { + height: 100%; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-page); +} + + +.list { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/es/components/user/manage/detail/web.d.ts b/es/components/user/manage/detail/web.d.ts new file mode 100644 index 000000000..241e6ffed --- /dev/null +++ b/es/components/user/manage/detail/web.d.ts @@ -0,0 +1,25 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../../oak-app-domain'; +export default function render(props: WebComponentProps; + idStateColor: Record; + mobileCount: number; + mobileText: string; + actionDescriptions: Record; + executableActions: string[]; +}, { + onActionClick: (action: string) => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/manage/detail/web.js b/es/components/user/manage/detail/web.js new file mode 100644 index 000000000..415c4b0c0 --- /dev/null +++ b/es/components/user/manage/detail/web.js @@ -0,0 +1,9 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { List, Tag, Avatar } from 'antd-mobile'; +import Style from './mobile.module.less'; +import ActionPanel from '../../../../components/func/actionPanel'; +export default function render(props) { + const { nickname, avatar, name, userState, idState, gender, stateColor, idStateColor, mobileText, executableActions, actionDescriptions, birth, } = props.data; + const { t, onActionClick } = props.methods; + return (_jsxs("div", { className: Style.container, children: [_jsxs(List, { className: Style.list, children: [_jsx(List.Item, { extra: avatar ? _jsx(Avatar, { src: avatar }) : t('unset'), children: t('avatar') }), _jsx(List.Item, { extra: nickname || t('unset'), children: t('user:attr.nickname') }), _jsx(List.Item, { extra: name || t('unset'), children: t('user:attr.name') }), _jsx(List.Item, { extra: gender ? t(`user:v.gender.${gender}`) : t('unset'), children: t('user:attr.gender') }), _jsx(List.Item, { extra: birth || t('unset'), children: t('user:attr.birth') }), _jsx(List.Item, { extra: mobileText, children: t('mobile') }), _jsx(List.Item, { extra: _jsx(Tag, { color: stateColor[userState], children: t(`user:v.userState.${userState}`) }), children: t('user:attr.userState') }), _jsx(List.Item, { extra: _jsx(Tag, { color: idStateColor[idState], children: t(`user:v.idState.${idState}`) }), children: t('user:attr.idState') })] }), _jsx(ActionPanel, { actions: executableActions, actionDescriptions: actionDescriptions, onActionClick: (action) => onActionClick(action) })] })); +} diff --git a/src/pages/mobile/me/web.module.less b/es/components/user/manage/detail/web.module.less similarity index 100% rename from src/pages/mobile/me/web.module.less rename to es/components/user/manage/detail/web.module.less diff --git a/es/components/user/manage/detail/web.pc.d.ts b/es/components/user/manage/detail/web.pc.d.ts new file mode 100644 index 000000000..241e6ffed --- /dev/null +++ b/es/components/user/manage/detail/web.pc.d.ts @@ -0,0 +1,25 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../../oak-app-domain'; +export default function render(props: WebComponentProps; + idStateColor: Record; + mobileCount: number; + mobileText: string; + actionDescriptions: Record; + executableActions: string[]; +}, { + onActionClick: (action: string) => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/manage/detail/web.pc.js b/es/components/user/manage/detail/web.pc.js new file mode 100644 index 000000000..e701bfe3e --- /dev/null +++ b/es/components/user/manage/detail/web.pc.js @@ -0,0 +1,10 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { Tag, Avatar, Descriptions } from 'antd'; +import Style from './web.module.less'; +import ActionPanel from '../../../../components/func/actionPanel'; +import PageHeader from '../../../../components/common/pageHeader'; +export default function render(props) { + const { nickname, avatar, name, userState, idState, gender, stateColor, idStateColor, mobileText, executableActions, actionDescriptions, birth, } = props.data; + const { t, onActionClick } = props.methods; + return (_jsx(PageHeader, { children: _jsx("div", { className: Style.container, children: _jsxs(Descriptions, { extra: _jsx(ActionPanel, { actions: executableActions, actionDescriptions: actionDescriptions, onActionClick: (action) => onActionClick(action) }), children: [_jsx(Descriptions.Item, { label: t('avatar'), children: avatar ? _jsx(Avatar, { src: avatar }) : t('unset') }), _jsx(Descriptions.Item, { label: t('user:attr.nickname'), children: nickname || t('unset') }), _jsx(Descriptions.Item, { label: t('user:attr.name'), children: name || t('unset') }), _jsx(Descriptions.Item, { label: t('user:attr.gender'), children: gender ? t(`user:v.gender.${gender}`) : t('unset') }), _jsx(Descriptions.Item, { label: t('user:attr.birth'), children: birth || t('unset') }), _jsx(Descriptions.Item, { label: t('mobile'), children: mobileText || t('unset') }), _jsx(Descriptions.Item, { label: t('user:attr.userState'), children: _jsx(Tag, { color: stateColor[userState], children: t(`user:v.userState.${userState}`) }) }), _jsx(Descriptions.Item, { label: t('user:attr.idState'), children: _jsx(Tag, { color: idStateColor[idState], children: t(`user:v.idState.${idState}`) }) })] }) }) })); +} diff --git a/es/components/user/manage/index.d.ts b/es/components/user/manage/index.d.ts new file mode 100644 index 000000000..0e4679827 --- /dev/null +++ b/es/components/user/manage/index.d.ts @@ -0,0 +1,5 @@ +/// +declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => import("react").ReactElement>; +export default _default; diff --git a/es/components/user/manage/index.js b/es/components/user/manage/index.js new file mode 100644 index 000000000..834428b54 --- /dev/null +++ b/es/components/user/manage/index.js @@ -0,0 +1,110 @@ +// index.ts +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + nickname: 1, + name: 1, + userState: 1, + idState: 1, + extraFile$entity: { + $entity: 'extraFile', + data: { + id: 1, + tag1: 1, + origin: 1, + bucket: 1, + objectId: 1, + filename: 1, + extra1: 1, + type: 1, + entity: 1, + entityId: 1, + extension: 1, + }, + filter: { + tag1: 'avatar', + }, + indexFrom: 0, + count: 1, + }, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + }, + }, + }, + sorters: [ + { + sorter: () => ({ + $attr: { + $$createAt$$: 1, + }, + $direction: 'desc', + }), + }, + ], + isList: true, + formData: function ({ data: users, features }) { + const userArr = users.map((user) => { + const { id, nickname, userState, name, mobile$user, extraFile$entity, } = user || {}; + const mobile = mobile$user && mobile$user[0]?.mobile; + const avatar = features.extraFile.getUrl(extraFile$entity && extraFile$entity[0]); + return { + id, + nickname, + name, + mobile, + avatar, + userState, + }; + }); + const isRoot = features.token.isReallyRoot(); + return { + userArr, + isRoot, + }; + }, + properties: { + event: '', + }, + data: { + stateColor: { + shadow: 'primary', + normal: 'success', + disabled: 'danger', + }, + }, + methods: { + async bindClicked(input) { + // resolveInput拿的是target,原来代码拿的是currentTarget + const { dataset } = this.resolveInput(input); + const { id } = dataset; + this.onCellClicked(id); + }, + async onCellClicked(id) { + const { event } = this.props; + if (event) { + this.pubEvent(event, this.state.userArr.find((ele) => ele.id === id)); + } + else { + this.navigateTo({ + url: '/user/manage/detail', + oakId: id, + }); + } + }, + goNewUser() { + this.navigateTo({ + url: '/user/manage/upsert', + }); + }, + }, + lifetimes: { + detached() { + this.unsubAllEvents(this.props.event); + }, + }, +}); diff --git a/es/components/user/manage/index.json b/es/components/user/manage/index.json new file mode 100644 index 000000000..dee95e0ca --- /dev/null +++ b/es/components/user/manage/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "用户管理", + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-tag": "@oak-frontend-base/miniprogram_npm/lin-ui/tag/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index" + } +} diff --git a/es/components/user/manage/index.less b/es/components/user/manage/index.less new file mode 100644 index 000000000..e95c0b12f --- /dev/null +++ b/es/components/user/manage/index.less @@ -0,0 +1,77 @@ +/** index.wxss **/ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.user-list { + flex: 1; + display: flex; + flex-direction: column; +} + +.user-info { + display: flex; + flex-direction: column; + flex: 1; + margin-left: 36rpx; +} + +.cell { + display: flex; + align-items: center; + padding: 26rpx; + border-bottom: 1px solid #e0e0e0; + background-color: #fff; +} + +.row { + display: flex; + align-items: center; + min-width: 300rpx; + justify-content: space-between; + margin-bottom: 20rpx; + + .primary { + background-color: @oak-color-primary; + } + + .success { + background-color: @oak-color-success; + } + + .danger { + background-color: @oak-color-error; + } +} + +.row2 { + display: flex; + align-items: center; + font-size: small; + .label { + min-width: 100rpx; + color: @oak-text-color-primary; + } + .value { + color: @oak-text-color-secondary; + } +} + +.add { + position: fixed; + right: 20rpx; + z-index: 10; + bottom: constant(safe-area-inset-bottom) !important; + /* 兼容 iOS < 11.2 */ + bottom: env(safe-area-inset-bottom) !important; + /* 兼容 iOS >= 11.2 */ +} \ No newline at end of file diff --git a/es/components/user/manage/index.xml b/es/components/user/manage/index.xml new file mode 100644 index 000000000..a01279b27 --- /dev/null +++ b/es/components/user/manage/index.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/es/components/user/manage/mobile.module.less b/es/components/user/manage/mobile.module.less new file mode 100644 index 000000000..943d1e418 --- /dev/null +++ b/es/components/user/manage/mobile.module.less @@ -0,0 +1,41 @@ + + +.container { + height: 100vh; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-page); +} + + +.avatar { + height: 56px; + width: 56px; + background-color: var(--oak-color-primary); + + .text { + color: #fff; + font-size: 16px; + } +} + +.description { + .row { + .label { + color: var(--oak-text-color-secondary); + font-size: 12px; + } + + .value { + color: var(--oak-text-color-primary); + font-size: 12px; + } + } +} + +.fab { + position: fixed; + bottom: 50px; + right: 10px; + z-index: 1; +} \ No newline at end of file diff --git a/es/components/user/manage/upsert/index.d.ts b/es/components/user/manage/upsert/index.d.ts new file mode 100644 index 000000000..c44e02e34 --- /dev/null +++ b/es/components/user/manage/upsert/index.d.ts @@ -0,0 +1,5 @@ +/// +/// +import { EntityDict } from "../../../../oak-app-domain"; +declare const _default: (props: import("oak-frontend-base").ReactComponentProps) => import("react").ReactElement>; +export default _default; diff --git a/es/components/user/manage/upsert/index.js b/es/components/user/manage/upsert/index.js new file mode 100644 index 000000000..977b09a46 --- /dev/null +++ b/es/components/user/manage/upsert/index.js @@ -0,0 +1,138 @@ +const GenderOptions = [ + { + value: 'male', label: '男', + }, + { + value: 'female', label: '女', + } +]; +const IDCardTypeOptions = [ + { + value: 'ID-Card', label: '身份证', + }, + { + value: 'passport', label: '护照', + }, + { + value: 'Mainland-passport', label: '港澳通行证', + } +]; +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + name: 1, + nickname: 1, + birth: 1, + gender: 1, + idCardType: 1, + idNumber: 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, + }, + }, + isList: false, + formData({ data: user }) { + const isRoot = this.features.token.isRoot(); + const { birth, gender, idCardType } = user || {}; + const birthDate = birth && new Date(birth); + const birthText = birthDate && birthDate.toLocaleDateString(); + const birthDayValue = birthDate && + `${birthDate.getFullYear()}-${birthDate.getMonth() + 1}-${birthDate.getDate()}`; + const genderOption = gender && GenderOptions.find((ele) => ele.value === gender); + const genderText = genderOption && genderOption.label; + const genderOptionIndex = genderOption && GenderOptions.indexOf(genderOption); + const idCardTypeOption = idCardType && + IDCardTypeOptions.find((ele) => ele.value === idCardType); + const idCardTypeText = idCardTypeOption && idCardTypeOption.label; + const idCardTypeOptionIndex = idCardTypeOption && IDCardTypeOptions.indexOf(idCardTypeOption); + const idCardTypeIndex = idCardType && IDCardTypeOptions.find((ele) => ele.value === gender); + const now = new Date(); + return Object.assign({}, user, { + isRoot, + birthText, + birthDayValue, + genderText, + idCardTypeText, + idCardTypeOptionIndex, + oldestBirthday: `${now.getFullYear() - 120}-01-01`, + today: `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`, + genderOptionIndex, + idCardTypeIndex, + }); + }, + data: { + birthEnd: '', + GenderOptions, + IDCardTypeOptions, + birthVisible: false, + }, + lifetimes: { + ready() { + const today = new Date(); + const birthEnd = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`; + this.setState({ birthEnd }); + }, + }, + methods: { + setValueMp(input) { + const { detail, target: { dataset }, } = input; + const { attr } = dataset; + const { value } = detail; + this.update({ [attr]: value }); + }, + async confirm() { + const { nickname } = this.state; + if (!nickname) { + this.setMessage({ + type: 'warning', + content: '请输入昵称' + }); + return; + } + await this.execute(); + this.navigateBack(); + }, + onBirthChange(e) { + const { detail: { value }, } = e; + const birth = new Date(value); + this.update({ birth }); + }, + onIdCardTypeChange(e) { + const { detail: { value: index }, } = e; + const { value } = IDCardTypeOptions[index]; + this.update({ + idCardType: value, + }); + }, + onGenderChange(e) { + const { detail: { value: index }, } = e; + const { value } = GenderOptions[index]; + this.update({ + gender: value, + }); + }, + setMobile() { + this.navigateTo({ + url: '/mobile/me', + }); + }, + }, +}); diff --git a/es/components/user/manage/upsert/index.json b/es/components/user/manage/upsert/index.json new file mode 100644 index 000000000..f6190f1e2 --- /dev/null +++ b/es/components/user/manage/upsert/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "编辑用户信息", + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-input": "@oak-frontend-base/miniprogram_npm/lin-ui/input/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "mobile-manage-list": "../../../../components/mobile/manageList/index", + "oak-extraFile-avatar": "../../../../components/extraFile/avatar/index" + } +} diff --git a/es/components/user/manage/upsert/index.less b/es/components/user/manage/upsert/index.less new file mode 100644 index 000000000..442707f4d --- /dev/null +++ b/es/components/user/manage/upsert/index.less @@ -0,0 +1,59 @@ +/** index.wxss **/ +@import "../../../../config/styles/mp/index.less"; +@import "../../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.list-box { + padding-left: 25rpx; + padding-right: 25rpx; +} + +.avatar { + margin: 10rpx 0; +} + +.col { + display: flex; + flex-direction: column; + background-color: #fff; + + .label { + color: @oak-text-color-primary; + } +} + +.list-item { + position: relative; + font-size: 28rpx; + color: #333; + min-height: 88rpx; + display: flex; + flex-direction: row; + align-items: center; + padding-right: 25rpx; + box-sizing: border-box; + + .label { + width: 100px; + display: flex; + flex-direction: row; + align-items: center; + height: 88rpx; + padding-left: 25rpx; + padding-right: 15rpx; + box-sizing: border-box; + } + + .mobile-box { + flex: 1; + } +} \ No newline at end of file diff --git a/es/components/user/manage/upsert/index.xml b/es/components/user/manage/upsert/index.xml new file mode 100644 index 000000000..7f899fc30 --- /dev/null +++ b/es/components/user/manage/upsert/index.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 手机号 + + + + + + + + + {{mobile || '未绑定'}} + + + + + + + + {{t('common::action.confirm')}} + + \ No newline at end of file diff --git a/es/components/user/manage/upsert/mobile.module.less b/es/components/user/manage/upsert/mobile.module.less new file mode 100644 index 000000000..bec04c19c --- /dev/null +++ b/es/components/user/manage/upsert/mobile.module.less @@ -0,0 +1,12 @@ + + +.container { + height: 100vh; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-container); +} + +.radio { + margin-right: 10px; +} diff --git a/es/components/user/manage/upsert/web.d.ts b/es/components/user/manage/upsert/web.d.ts new file mode 100644 index 000000000..6ebf244c3 --- /dev/null +++ b/es/components/user/manage/upsert/web.d.ts @@ -0,0 +1,20 @@ +import { EntityDict } from '../../../../oak-app-domain'; +import { WebComponentProps } from 'oak-frontend-base'; +export default function Render(props: WebComponentProps; + IDCardTypeOptions: Array<{ + value: string; + label: string; + }>; +}, { + confirm: () => void; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/manage/upsert/web.js b/es/components/user/manage/upsert/web.js new file mode 100644 index 000000000..92ae46a1e --- /dev/null +++ b/es/components/user/manage/upsert/web.js @@ -0,0 +1,31 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState } from 'react'; +import { Button, Input, Form, Radio, DatePicker, Space } from 'antd-mobile'; +import dayjs from 'dayjs'; +import Style from './mobile.module.less'; +export default function Render(props) { + const { data, methods } = props; + const { GenderOptions, IDCardTypeOptions } = data; + const { t, update, setDisablePulldownRefresh, confirm } = methods; + const [birthPickerVisible, setBirthPickerVisible] = useState(false); + return (_jsxs("div", { className: Style.container, children: [_jsxs(Form, { layout: "horizontal", children: [_jsx(Form.Item, { label: t('user:attr.nickname'), rules: [{ required: true }], children: _jsx(Input, { onChange: (val) => update({ nickname: val }), value: data.nickname || '' }) }), _jsx(Form.Item, { label: t('user:attr.name'), children: _jsx(Input, { onChange: (val) => update({ name: val }), value: data.name || '' }) }), _jsx(Form.Item, { label: t('user:attr.birth'), onClick: () => { + setBirthPickerVisible(true); + setDisablePulldownRefresh(true); + }, children: _jsx(Input, { value: data.birth + ? dayjs(data.birth).format('YYYY-MM-DD') + : '', readOnly: true }) }), _jsx(Form.Item, { label: t('user:attr.gender'), children: _jsx(Radio.Group, { onChange: (e) => { + update({ + gender: e, + }); + }, value: data.gender, children: _jsx(Space, { direction: "horizontal", children: GenderOptions.map((ele, idx) => (_jsx(Radio, { value: ele.value, className: Style.radio, children: ele.label }, idx))) }) }) }), _jsx(Form.Item, { label: t('user:attr.idCardType'), children: _jsx(Radio.Group, { onChange: (e) => { + update({ + idCardType: e, + }); + }, value: data.idCardType, children: _jsx(Space, { direction: "vertical", children: IDCardTypeOptions.map((ele, idx) => (_jsx(Radio, { value: ele.value, className: Style.radio, children: ele.label }, idx))) }) }) }), _jsx(Form.Item, { label: t('user:attr.idNumber'), children: _jsx(Input, { onChange: (val) => update({ idNumber: val }), value: data.idNumber || '' }) })] }), _jsx(DatePicker, { visible: birthPickerVisible, max: new Date(), min: new Date('1900-01-01'), onConfirm: (value) => { + const val = value.valueOf(); + update({ birth: val }); + }, onClose: () => { + setBirthPickerVisible(false); + setDisablePulldownRefresh(false); + } }), _jsx("div", { style: { flex: 1 } }), _jsx(Button, { block: true, color: "primary", onClick: () => confirm(), children: t('common::action.confirm') })] })); +} diff --git a/src/pages/userEntityGrant/detail/web.module.less b/es/components/user/manage/upsert/web.module.less similarity index 98% rename from src/pages/userEntityGrant/detail/web.module.less rename to es/components/user/manage/upsert/web.module.less index 1903c799a..0dad10def 100644 --- a/src/pages/userEntityGrant/detail/web.module.less +++ b/es/components/user/manage/upsert/web.module.less @@ -5,4 +5,4 @@ box-shadow: 0 2px 3px #0000001a; border-radius: 3px; padding: 30px 32px; -} +} \ No newline at end of file diff --git a/es/components/user/manage/upsert/web.pc.d.ts b/es/components/user/manage/upsert/web.pc.d.ts new file mode 100644 index 000000000..dbf9df8c9 --- /dev/null +++ b/es/components/user/manage/upsert/web.pc.d.ts @@ -0,0 +1,20 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../../oak-app-domain'; +export default function Render(props: WebComponentProps; + IDCardTypeOptions: Array<{ + value: string; + label: string; + }>; +}, { + confirm: () => void; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/manage/upsert/web.pc.js b/es/components/user/manage/upsert/web.pc.js new file mode 100644 index 000000000..af1ea5dd9 --- /dev/null +++ b/es/components/user/manage/upsert/web.pc.js @@ -0,0 +1,21 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { Button, Input, Form, Radio, DatePicker } from 'antd'; +import dayjs from 'dayjs'; +import PageHeader from '../../../../components/common/pageHeader'; +import Style from './web.module.less'; +export default function Render(props) { + const { data, methods } = props; + const { GenderOptions, IDCardTypeOptions } = data; + const { t, update, confirm } = methods; + return (_jsx(PageHeader, { children: _jsx("div", { className: Style.container, children: _jsxs(Form, { layout: "horizontal", labelCol: { span: 8 }, wrapperCol: { span: 16 }, style: { maxWidth: 600 }, children: [_jsx(Form.Item, { label: t('user:attr.nickname'), required: true, children: _jsx(Input, { onChange: (e) => update({ nickname: e.target.value }), value: data.nickname || '' }) }), _jsx(Form.Item, { label: t('user:attr.name'), children: _jsx(Input, { onChange: (e) => update({ name: e.target.value }), value: data.name || '' }) }), _jsx(Form.Item, { label: t('user:attr.birth'), children: _jsx(DatePicker, { value: data.birth ? dayjs(data.birth) : undefined, format: 'YYYY/MM/DD', onChange: (value) => update({ birth: dayjs(value).valueOf() }) }) }), _jsx(Form.Item, { label: t('user:attr.gender'), children: _jsx(Radio.Group, { onChange: (e) => { + update({ + gender: e.target + .value, + }); + }, value: data.gender, children: GenderOptions.map((ele, idx) => (_jsx(Radio, { value: ele.value, children: ele.label }, idx))) }) }), _jsx(Form.Item, { label: t('user:attr.idCardType'), children: _jsx(Radio.Group, { onChange: (e) => { + update({ + idCardType: e.target + .value, + }); + }, value: data.idCardType, children: IDCardTypeOptions.map((ele, idx) => (_jsx(Radio, { value: ele.value, className: Style.radio, children: ele.label }, idx))) }) }), _jsx(Form.Item, { label: t('user:attr.idNumber'), children: _jsx(Input, { onChange: (e) => update({ idNumber: e.target.value }), value: data.idNumber || '' }) }), _jsx(Form.Item, { wrapperCol: { offset: 8, span: 16 }, children: _jsx(Button, { type: "primary", color: "primary", onClick: () => confirm(), children: t('common::action.confirm') }) })] }) }) })); +} diff --git a/es/components/user/manage/web.d.ts b/es/components/user/manage/web.d.ts new file mode 100644 index 000000000..0daf53dfd --- /dev/null +++ b/es/components/user/manage/web.d.ts @@ -0,0 +1,13 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function render(props: WebComponentProps; + stateColor: Record; + isRoot: boolean; +}, { + onCellClicked: (id: string) => Promise; + goNewUser: () => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/manage/web.js b/es/components/user/manage/web.js new file mode 100644 index 000000000..4d731d630 --- /dev/null +++ b/es/components/user/manage/web.js @@ -0,0 +1,19 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { List, Tag, Avatar, FloatingBubble } from 'antd-mobile'; +import { PlusOutlined } from '@ant-design/icons'; +import Style from './mobile.module.less'; +export default function render(props) { + const { stateColor, userArr, isRoot } = props.data; + const { onCellClicked, t, goNewUser } = props.methods; + return (_jsxs("div", { className: Style.container, children: [_jsx(List, { children: userArr?.map((ele, index) => { + return (_jsx(List.Item, { onClick: () => onCellClicked(ele.id), prefix: _jsx(Avatar, { className: Style.avatar, src: ele.avatar }), title: _jsx("div", { children: ele.name || '--' }), description: _jsxs("div", { className: Style.description, children: [_jsxs("div", { className: Style.row, children: [_jsx("span", { className: Style.label, children: "\u6635\u79F0:\u00A0" }), _jsx("span", { className: Style.value, children: ele.nickname || '--' })] }), _jsxs("div", { className: Style.row, children: [_jsx("span", { className: Style.label, children: "\u624B\u673A\u53F7:\u00A0" }), _jsx("span", { className: Style.value, children: ele.mobile || '--' })] }), _jsx(Tag, { color: stateColor[ele.userState], children: ele.userState + ? t(`user:v.userState.${ele.userState}`) + : '未知' })] }) }, index)); + }) }), isRoot && (_jsx(FloatingBubble, { axis: "x", magnetic: "x", style: { + '--initial-position-bottom': '24px', + '--initial-position-right': '24px', + '--edge-distance': '24px', + }, onClick: () => { + goNewUser(); + }, children: _jsx(PlusOutlined, {}) }))] })); +} diff --git a/es/components/user/manage/web.module.less b/es/components/user/manage/web.module.less new file mode 100644 index 000000000..0dad10def --- /dev/null +++ b/es/components/user/manage/web.module.less @@ -0,0 +1,8 @@ + + +.container { + background: var(--oak-bg-color-container); + box-shadow: 0 2px 3px #0000001a; + border-radius: 3px; + padding: 30px 32px; +} \ No newline at end of file diff --git a/es/components/user/manage/web.pc.d.ts b/es/components/user/manage/web.pc.d.ts new file mode 100644 index 000000000..e951cdfda --- /dev/null +++ b/es/components/user/manage/web.pc.d.ts @@ -0,0 +1,13 @@ +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +export default function Render(props: WebComponentProps; + stateColor: Record; + isRoot: boolean; +}, { + onCellClicked: (id: string, event?: string) => Promise; + goNewUser: () => Promise; +}>): import("react/jsx-runtime").JSX.Element; diff --git a/es/components/user/manage/web.pc.js b/es/components/user/manage/web.pc.js new file mode 100644 index 000000000..c6caeb391 --- /dev/null +++ b/es/components/user/manage/web.pc.js @@ -0,0 +1,87 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +import { Table, Tag, Button, Space, Avatar } from 'antd'; +import { UserOutlined } from '@ant-design/icons'; +import FilterPanel from 'oak-frontend-base/es/components/filterPanel'; +import PageHeader from '../../../components/common/pageHeader'; +import Style from './web.module.less'; +export default function Render(props) { + const { methods, data } = props; + const { t, setPageSize, setCurrentPage, goNewUser, onCellClicked } = methods; + const { oakFullpath, oakLoading, oakPagination, userArr = [], stateColor, isRoot, } = data; + const { pageSize, total, currentPage } = oakPagination || {}; + return (_jsx(PageHeader, { children: _jsxs("div", { className: Style.container, children: [isRoot && (_jsx(Space, { style: { marginBottom: 16 }, children: _jsx(Button, { type: "primary", onClick: () => { + goNewUser(); + }, children: "\u6DFB\u52A0\u7528\u6237" }) })), _jsx(FilterPanel, { entity: "user", oakPath: oakFullpath, columns: [ + { + attr: 'nickname', + op: '$includes', + }, + { + attr: 'name', + op: '$includes', + }, + { + attr: 'userState', + }, + ] }), _jsx(Table, { loading: oakLoading, dataSource: userArr, rowKey: "id", columns: [ + { + dataIndex: 'id', + title: '#', + render: (value, record, index) => { + return index + 1; + }, + }, + { + width: 100, + dataIndex: 'avatar', + title: '头像', + render: (value, record, index) => { + if (!value) { + return (_jsx(Avatar, { icon: _jsx(UserOutlined, {}) })); + } + return _jsx(Avatar, { src: value, shape: "circle" }); + }, + }, + { + dataIndex: 'nickname', + title: '昵称', + }, + { + dataIndex: 'name', + title: '姓名', + }, + { + dataIndex: 'mobile', + title: '手机号', + }, + { + dataIndex: 'userState', + title: '状态', + render: (value, record, index) => { + return (_jsx(Tag, { color: stateColor[value], children: t(`user:v.userState.${value}`) })); + }, + }, + { + dataIndex: 'op', + width: 200, + title: '操作', + align: 'center', + render: (value, record, index) => { + return (_jsx(_Fragment, { children: _jsx(Button, { type: "link", onClick: () => { + onCellClicked(record.id); + }, children: "\u8BE6\u60C5" }) })); + }, + fixed: 'right', + }, + ], pagination: { + total: total, + pageSize: pageSize, + current: currentPage, + onShowSizeChange: (pageSize) => { + setPageSize(pageSize); + }, + onChange: (page) => { + setCurrentPage(page); + }, + } })] }) })); +} diff --git a/es/components/userEntityGrant/claim/index.json b/es/components/userEntityGrant/claim/index.json index 0967ef424..0668239e2 100644 --- a/es/components/userEntityGrant/claim/index.json +++ b/es/components/userEntityGrant/claim/index.json @@ -1 +1,8 @@ -{} +{ + "navigationBarTitleText": "领取权限", + "usingComponents": { + "l-notice-bar": "@oak-frontend-base/miniprogram_npm/lin-ui/notice-bar/index", + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index" + } +} diff --git a/es/components/userEntityGrant/claim/index.less b/es/components/userEntityGrant/claim/index.less new file mode 100644 index 000000000..db228d791 --- /dev/null +++ b/es/components/userEntityGrant/claim/index.less @@ -0,0 +1,20 @@ +/** index.wxss **/ +@import '../../../config/styles/mp/index.less'; +@import '../../../config/styles/mp/mixins.less'; + +.view { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.btn { + position: fixed; + bottom: 0; + width: 100%; + // margin: 0 10px; +} diff --git a/es/components/userEntityGrant/claim/index.xml b/es/components/userEntityGrant/claim/index.xml index e69de29bb..e87446d58 100644 --- a/es/components/userEntityGrant/claim/index.xml +++ b/es/components/userEntityGrant/claim/index.xml @@ -0,0 +1,32 @@ + + + + + {{t('tip')}} + + + {{t('isGranter')}} + + + {{t('hasClaimed')}} + + + + + + + + + + + + + + + {{isGranter ? t('isGranter') : ( hasClaimed ? t('hasClaimed') : (!expired ? t('userEntityGrant:action.claim') : t('expired')))}} + + + \ No newline at end of file diff --git a/es/components/userEntityGrant/list/index.js b/es/components/userEntityGrant/list/index.js index c06778ca6..1b5c7505b 100644 --- a/es/components/userEntityGrant/list/index.js +++ b/es/components/userEntityGrant/list/index.js @@ -17,11 +17,13 @@ export default OakComponent({ name: 1, nickname: 1, }, - granteeId: 1, $$createAt$$: 1, expired: 1, expiresAt: 1, }, + actions: [ + 'disable', + ], properties: { entity: '', entityId: '', diff --git a/es/components/userEntityGrant/list/web.js b/es/components/userEntityGrant/list/web.js index c059d30b0..197606f76 100644 --- a/es/components/userEntityGrant/list/web.js +++ b/es/components/userEntityGrant/list/web.js @@ -1,93 +1,105 @@ -import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime"; -import { Table, Space, Typography } from 'antd'; +import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime"; +import { useState } from 'react'; +import { Table, Space, Typography, Modal } from 'antd'; import dayjs from 'dayjs'; import ActionBtnPanel from 'oak-frontend-base/es/components/actionBtnPanel'; +import UserEntityGrantShare from '../share'; export default function render(props) { const { oakPagination, oakFullpath, list = [], oakLoading, } = props.data; const { pageSize, total, currentPage } = oakPagination || {}; const { t, setPageSize, setCurrentPage, execute, updateItem } = props.methods; - return (_jsx(Table, { loading: oakLoading, dataSource: list, rowKey: "id", columns: [ - { - dataIndex: 'id', - title: '#', - render: (value, record, index) => { - return index + 1; - }, - }, - { - dataIndex: 'name', - title: '授权人', - render: (value, record, index) => { - return (_jsxs(Space, { direction: "vertical", children: [_jsxs(Typography.Text, { children: ["\u59D3\u540D\uFF1A", record.granter?.name || '-'] }), _jsxs(Typography.Text, { children: ["\u6635\u79F0\uFF1A", record.granter?.nickname] })] })); - }, - }, - { - dataIndex: 'type', - title: '授权类型', - render: (value, record, index) => { - return t(`userEntityGrant:v.type.${value}`); - }, - }, - { - dataIndex: 'relation', - title: '权限', - render: (value, record, index) => { - return (value.display || - t(`${record.relationEntity}:r.${value.name}`)); - }, - }, - { - dataIndex: '$$createAt$$', - title: '创建时间', - render: (value, record, index) => { - return dayjs(value).format('YYYY-MM-DD HH:mm'); - }, - }, - { - dataIndex: 'expired', - title: '状态', - render: (value, record, index) => { - return (_jsxs(Typography.Text, { type: record.expired ? 'danger' : 'success', children: [record.expired ? '失效' : '有效', !record.expired && (_jsxs(Typography.Text, { children: ["\u00A0", dayjs(record.expiresAt).format('YYYY-MM-DD HH:mm')] }))] })); - }, - }, - { - width: 200, - title: '操作', - key: 'action', - align: 'center', - fixed: 'right', - render: (value, record, rowIndex) => { - return (_jsx(ActionBtnPanel, { mode: "table-cell", entity: "parasite", items: [ - { - label: '失效', - action: 'disable', - // alerted: true, - show: record['#oakLegalActions']?.includes('cancel'), - onClick: () => { - updateItem({ expired: true }, record.id, 'disable'); - execute(); - }, - }, - { - label: '二维码', - action: 'qrcode', - show: record['#oakLegalActions']?.includes('qrcode'), - // alerted: true, - onClick: async () => { - }, - }, - ] })); - }, - }, - ], pagination: { - total, - pageSize, - current: currentPage, - onShowSizeChange: (pageSize) => { - setPageSize(pageSize); - }, - onChange: (current) => { - setCurrentPage(current); - }, - } })); + const [qrCodeOpen, setQrCodeOpen] = useState(false); + const [userEntityGrantId, setUserEntityGrantId] = useState(''); + return (_jsxs(_Fragment, { children: [_jsx(Table, { loading: oakLoading, dataSource: list, rowKey: "id", columns: [ + { + dataIndex: 'id', + title: '#', + render: (value, record, index) => { + return index + 1; + }, + }, + { + dataIndex: 'name', + title: '授权人', + render: (value, record, index) => { + return (_jsxs(Space, { direction: "vertical", children: [_jsxs(Typography.Text, { children: ["\u59D3\u540D\uFF1A", record.granter?.name || '-'] }), _jsxs(Typography.Text, { children: ["\u6635\u79F0\uFF1A", record.granter?.nickname] })] })); + }, + }, + { + dataIndex: 'type', + title: '授权类型', + render: (value, record, index) => { + return t(`userEntityGrant:v.type.${value}`); + }, + }, + // { + // dataIndex: 'relation', + // title: '权限', + // render: (value, record, index) => { + // return ( + // // value.display || + // t(`${record.relationEntity}:r.${value.name}`) + // ); + // }, + // }, + { + dataIndex: '$$createAt$$', + title: '创建时间', + render: (value, record, index) => { + return dayjs(value).format('YYYY-MM-DD HH:mm'); + }, + }, + { + dataIndex: 'expired', + title: '状态', + render: (value, record, index) => { + return (_jsxs(Typography.Text, { type: record.expired ? 'danger' : 'success', children: [record.expired ? '失效' : '有效', !record.expired && (_jsxs(Typography.Text, { children: ["\u00A0", dayjs(record.expiresAt).format('YYYY-MM-DD HH:mm')] }))] })); + }, + }, + { + width: 200, + title: '操作', + key: 'action', + align: 'center', + fixed: 'right', + render: (value, record, rowIndex) => { + return (_jsx(ActionBtnPanel, { mode: "table-cell", entity: "userEntityGrant", items: [ + { + label: '失效', + action: 'disable', + // alerted: true, + show: record['#oakLegalActions']?.includes('disable'), + onClick: () => { + updateItem({ expired: true }, record.id, 'disable'); + execute(); + }, + }, + { + label: '二维码', + // action: 'qrcode', + // show: record[ + // '#oakLegalActions' + // ]?.includes('qrcode'), + // alerted: true, + onClick: async () => { + setUserEntityGrantId(record.id); + setQrCodeOpen(true); + }, + }, + ] })); + }, + }, + ], pagination: { + total, + pageSize, + current: currentPage, + onShowSizeChange: (pageSize) => { + setPageSize(pageSize); + }, + onChange: (current) => { + setCurrentPage(current); + }, + } }), _jsx(Modal, { width: 786, open: qrCodeOpen, destroyOnClose: true, onCancel: () => { + setQrCodeOpen(false); + }, footer: null, children: _jsx(UserEntityGrantShare, { oakId: userEntityGrantId, oakAutoUnmount: true, oakPath: "$userEntityGrant/list-userEntityGrant/detail" }) })] })); } diff --git a/es/components/userEntityGrant/share/index.xml b/es/components/userEntityGrant/share/index.xml index 61bedafc6..6685d8f5a 100644 --- a/es/components/userEntityGrant/share/index.xml +++ b/es/components/userEntityGrant/share/index.xml @@ -1,11 +1,5 @@ - diff --git a/es/components/userEntityGrant/upsert/index.d.ts b/es/components/userEntityGrant/upsert/index.d.ts index 0eab75acc..cd3cb5a0a 100644 --- a/es/components/userEntityGrant/upsert/index.d.ts +++ b/es/components/userEntityGrant/upsert/index.d.ts @@ -6,7 +6,7 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps< entityId: string; relationEntity: string; relationEntityFilter: any; - relations: import("../../../oak-app-domain/Relation/Schema").OpSchema[]; + relationIds: string[]; type: "grant" | "transfer"; redirectToAfterConfirm: import("../../../oak-app-domain/UserEntityGrant/Schema").RedirectToProps | null | undefined; claimUrl: string; diff --git a/es/components/userEntityGrant/upsert/index.js b/es/components/userEntityGrant/upsert/index.js index 7f17ec895..89effb337 100644 --- a/es/components/userEntityGrant/upsert/index.js +++ b/es/components/userEntityGrant/upsert/index.js @@ -22,7 +22,7 @@ export default OakComponent({ entityId: '', relationEntity: '', relationEntityFilter: {}, - relations: [], + relationIds: [], type: 'grant', redirectToAfterConfirm: {}, claimUrl: '', @@ -44,13 +44,17 @@ export default OakComponent({ }, setInit() { const userId = this.features.token.getUserId(); - const { entity, entityId, relationEntity, relationEntityFilter, type, redirectToAfterConfirm, qrCodeType, claimUrl, multiple, rule, ruleOnRow, } = this.props; + const { entity, entityId, relationEntity, relationEntityFilter, relationIds, type, redirectToAfterConfirm, qrCodeType, claimUrl, multiple, rule, ruleOnRow, } = this.props; + this.setState({ + userEntityGrantId: '', + }); if (this.isCreation()) { this.update({ entity, entityId, relationEntity, relationEntityFilter, + relationIds, type: type || 'grant', multiple, rule: rule || 'single', @@ -60,9 +64,9 @@ export default OakComponent({ qrCodeType: qrCodeType, claimUrl, }); - this.setState({ - userEntityGrantId: '', - }); + // this.setState({ + // userEntityGrantId: '', + // }); } }, async confirm() { @@ -87,5 +91,5 @@ export default OakComponent({ userEntityGrantId: id, }); }, - } + }, }); diff --git a/es/components/userEntityGrant/upsert/web.pc.js b/es/components/userEntityGrant/upsert/web.pc.js index 4db4f4e19..7fc5b374f 100644 --- a/es/components/userEntityGrant/upsert/web.pc.js +++ b/es/components/userEntityGrant/upsert/web.pc.js @@ -1,11 +1,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { Form, Space, Button, InputNumber, Typography, } from 'antd'; import UserEntityGrantShare from '../share'; -import PageHeader from '../../common/pageHeader'; import Style from './web.module.less'; export default function Render(props) { const { oakId, oakFullpath, entity, entityId, relationEntity, showBack = true, userEntityGrantId, period } = props.data; const { setPeriod, confirm, setInit } = props.methods; + console.log(userEntityGrantId); if (!!userEntityGrantId) { return (_jsxs("div", { className: Style.container, children: [_jsx(UserEntityGrantShare, { oakId: userEntityGrantId, oakAutoUnmount: true, oakPath: "$userEntityGrant/upsert-userEntityGrant/detail" }), _jsx("div", { style: { width: '100%', @@ -15,9 +15,9 @@ export default function Render(props) { setInit(); }, children: "\u91CD\u65B0\u751F\u6210" }) })] })); } - return (_jsx(PageHeader, { title: "\u521B\u5EFA", showBack: showBack, children: _jsx("div", { className: Style.container, children: _jsxs(Form, { labelCol: { span: 4 }, wrapperCol: { span: 8 }, children: [_jsx(Form.Item, { label: "\u6709\u6548\u671F", required: true, children: _jsx(_Fragment, { children: _jsx(InputNumber, { min: 1, max: 30, placeholder: "\u8BF7\u8F93\u5165", onChange: (value) => { - setPeriod(value); - }, value: period, addonAfter: _jsx(Typography, { children: "\u5929" }) }) }) }), _jsx(Form.Item, { wrapperCol: { offset: 4 }, children: _jsx(Space, { children: _jsx(Button, { type: "primary", onClick: () => { - confirm(); - }, children: "\u63D0\u4EA4" }) }) })] }) }) })); + return (_jsx("div", { className: Style.container, children: _jsxs(Form, { labelCol: { span: 4 }, wrapperCol: { span: 8 }, children: [_jsx(Form.Item, { label: "\u6709\u6548\u671F", required: true, children: _jsx(_Fragment, { children: _jsx(InputNumber, { min: 1, max: 30, placeholder: "\u8BF7\u8F93\u5165", onChange: (value) => { + setPeriod(value); + }, value: period, addonAfter: _jsx(Typography, { children: "\u5929" }) }) }) }), _jsx(Form.Item, { wrapperCol: { offset: 4 }, children: _jsx(Space, { children: _jsx(Button, { type: "primary", onClick: () => { + confirm(); + }, children: "\u63D0\u4EA4" }) }) })] }) })); } diff --git a/es/components/userRelation/upsert/byMobile/index.xml b/es/components/userRelation/upsert/byMobile/index.xml index 40ba9d0ca..217371698 100644 --- a/es/components/userRelation/upsert/byMobile/index.xml +++ b/es/components/userRelation/upsert/byMobile/index.xml @@ -23,7 +23,7 @@ style="display: flex;flex-direction: column; flex: 1; margin-top: 30rpx" oakId="{{userId}}" mobile="{{mobileValue}}" - oakPath="{{oakFullpath ? oakFullpath + '.user' : undefined}}" + oakPath="{{oakFullpath + '.user'}}" oakAutoUnmount="{{true}}" entity="{{entity}}" entityId="{{entityId}}" diff --git a/es/components/userRelation/upsert/byMobile/web.js b/es/components/userRelation/upsert/byMobile/web.js index 3796834f1..f456d743c 100644 --- a/es/components/userRelation/upsert/byMobile/web.js +++ b/es/components/userRelation/upsert/byMobile/web.js @@ -22,5 +22,5 @@ export default function Render(props) { }, ], children: _jsx(_Fragment, { children: _jsx(Input, { maxLength: 11, value: mobileValue, onChange: (value) => { onMobileChange(value); - }, placeholder: t('inputMobile'), type: "tel", clearable: true }) }) }), mobileValueReady && userId && (_jsx(OnUser, { oakAutoUnmount: true, oakPath: oakFullpath ? `${oakFullpath}.user` : undefined, entity: entity, entityId: entityId, relations: relations, oakId: userId }))] })); + }, placeholder: t('inputMobile'), type: "tel", clearable: true }) }) }), mobileValueReady && userId && (_jsx(OnUser, { oakAutoUnmount: true, oakPath: `${oakFullpath}.user`, entity: entity, entityId: entityId, relations: relations, oakId: userId }))] })); } diff --git a/es/components/userRelation/upsert/byMobile/web.pc.js b/es/components/userRelation/upsert/byMobile/web.pc.js index 7da708d2f..03518607a 100644 --- a/es/components/userRelation/upsert/byMobile/web.pc.js +++ b/es/components/userRelation/upsert/byMobile/web.pc.js @@ -24,7 +24,7 @@ export default function Render(props) { ], children: _jsx(Input, { maxLength: 11, value: mobileValue, onChange: (e) => { const strValue = e.target.value; onMobileChange(strValue); - }, placeholder: "\u8BF7\u8F93\u5165\u624B\u673A\u53F7\u7801", type: "tel" }) }) }), mobileValueReady && userId && (_jsx(OnUser, { oakAutoUnmount: true, oakPath: oakFullpath ? `${oakFullpath}.user` : undefined, entity: entity, entityId: entityId, relations: relations, oakId: userId, setPasswordConfirm: setPasswordConfirm })), _jsx(Form, { colon: true, labelCol: { span: 4 }, wrapperCol: { span: 8 }, children: _jsx(Form.Item, { wrapperCol: { offset: 4 }, children: _jsxs(Space, { children: [_jsx(Button, { style: { flex: 2 }, type: "primary", htmlType: "reset", onClick: async () => { + }, placeholder: "\u8BF7\u8F93\u5165\u624B\u673A\u53F7\u7801", type: "tel" }) }) }), mobileValueReady && userId && (_jsx(OnUser, { oakAutoUnmount: true, oakPath: `${oakFullpath}.user`, entity: entity, entityId: entityId, relations: relations, oakId: userId, setPasswordConfirm: setPasswordConfirm })), _jsx(Form, { colon: true, labelCol: { span: 4 }, wrapperCol: { span: 8 }, children: _jsx(Form.Item, { wrapperCol: { offset: 4 }, children: _jsxs(Space, { children: [_jsx(Button, { style: { flex: 2 }, type: "primary", htmlType: "reset", onClick: async () => { await onConfirm(); setPasswordConfirm(true); }, disabled: !legal || diff --git a/es/components/userRelation/upsert/byUser/index.xml b/es/components/userRelation/upsert/byUser/index.xml index 2e5a4a192..61135a370 100644 --- a/es/components/userRelation/upsert/byUser/index.xml +++ b/es/components/userRelation/upsert/byUser/index.xml @@ -1,5 +1,5 @@ - + diff --git a/es/components/userRelation/upsert/byUser/web.js b/es/components/userRelation/upsert/byUser/web.js index 0d036fe3d..3c6c3ce49 100644 --- a/es/components/userRelation/upsert/byUser/web.js +++ b/es/components/userRelation/upsert/byUser/web.js @@ -6,5 +6,5 @@ export default function Render(props) { const { onConfirm, onReset, t } = props.methods; return (_jsx(Form, { footer: _jsxs(Space, { children: [_jsx(Button, { color: "primary", style: { flex: 2 }, onClick: () => { onConfirm(); - }, disabled: oakExecutable !== true, children: t('common::action.confirm') }), _jsx(Button, { style: { flex: 1 }, onClick: () => onReset(), children: t('common::reset') })] }), children: _jsx(OnUser, { oakAutoUnmount: true, oakPath: oakFullpath && `${oakFullpath}.user`, entity: entity, entityId: entityId, relations: relations, oakId: oakId }) })); + }, disabled: oakExecutable !== true, children: t('common::action.confirm') }), _jsx(Button, { style: { flex: 1 }, onClick: () => onReset(), children: t('common::reset') })] }), children: _jsx(OnUser, { oakAutoUnmount: true, oakPath: `${oakFullpath}.user`, entity: entity, entityId: entityId, relations: relations, oakId: oakId }) })); } diff --git a/es/components/userRelation/upsert/byUser/web.pc.js b/es/components/userRelation/upsert/byUser/web.pc.js index f4e9eb215..77f0c139b 100644 --- a/es/components/userRelation/upsert/byUser/web.pc.js +++ b/es/components/userRelation/upsert/byUser/web.pc.js @@ -4,5 +4,5 @@ import OnUser from '../onUser/index'; export default function Render(props) { const { relations, entity, entityId, oakId, oakDirty, oakFullpath } = props.data; const { onConfirm, onReset, t } = props.methods; - return (_jsxs(_Fragment, { children: [_jsx(OnUser, { oakAutoUnmount: true, oakPath: oakFullpath && `${oakFullpath}.user`, entity: entity, entityId: entityId, relations: relations, oakId: oakId }), _jsx(Form, { colon: true, labelCol: { span: 4 }, wrapperCol: { span: 8 }, children: _jsx(Form.Item, { wrapperCol: { offset: 4 }, children: _jsxs(Space, { children: [_jsx(Button, { disabled: !oakDirty, type: "primary", onClick: () => onConfirm(), children: t('common::action.confirm') }), _jsx(Button, { htmlType: "reset", onClick: () => onReset(), children: t('common::reset') })] }) }) })] })); + return (_jsxs(_Fragment, { children: [_jsx(OnUser, { oakAutoUnmount: true, oakPath: `${oakFullpath}.user`, entity: entity, entityId: entityId, relations: relations, oakId: oakId }), _jsx(Form, { colon: true, labelCol: { span: 4 }, wrapperCol: { span: 8 }, children: _jsx(Form.Item, { wrapperCol: { offset: 4 }, children: _jsxs(Space, { children: [_jsx(Button, { disabled: !oakDirty, type: "primary", onClick: () => onConfirm(), children: t('common::action.confirm') }), _jsx(Button, { htmlType: "reset", onClick: () => onReset(), children: t('common::reset') })] }) }) })] })); } diff --git a/es/components/userRelation/upsert/byUserEntityGrant/index.js b/es/components/userRelation/upsert/byUserEntityGrant/index.js index dc9698954..19b27cfab 100644 --- a/es/components/userRelation/upsert/byUserEntityGrant/index.js +++ b/es/components/userRelation/upsert/byUserEntityGrant/index.js @@ -15,9 +15,11 @@ export default OakComponent({ qrCodeType: 1, }, isList: false, - formData: ({ data: userEntityGrant }) => ({ - userEntityGrant, - }), + formData({ data: userEntityGrant, props }) { + return { + userEntityGrant, + }; + }, properties: { entity: '', entityId: '', @@ -106,12 +108,26 @@ export default OakComponent({ }, setRelation(value) { this.update({ - relationIds: [value], + relationIds: value, }); }, setRelationMp(e) { - const { currentKey } = e.detail; - this.setRelation(currentKey); + const { key } = e.detail; + const { userEntityGrant } = this.state; + const relationIds = [...(userEntityGrant?.relationIds || [])]; + const index = relationIds.findIndex((ele) => ele === key); + if (index > -1) { + relationIds.splice(index, 1); + } + else { + relationIds.push(key); + } + // 小程序 多选处理 + const newRelations = this.props.relations?.map((ele) => Object.assign({}, ele, { + checked: relationIds.includes(ele.id), + })); + this.setState({ relations: newRelations }); + this.setRelation(relationIds); }, setNumber(value) { this.update({ @@ -129,6 +145,20 @@ export default OakComponent({ const { count } = e.detail; this.setPeriod(count); }, + setMultiple(m) { + this.update({ multiple: m }); + }, + setMultipleMp(e) { + const { value } = e.detail; + this.setMultiple(value); + }, + setRule(m) { + this.update({ rule: m }); + }, + setRuleMp(e) { + const { currentKey } = e.detail; + this.setRule(currentKey); + }, setUnit(u) { const { defaultPeriods } = this.state; this.setState({ unit: u }); diff --git a/es/components/userRelation/upsert/byUserEntityGrant/index.json b/es/components/userRelation/upsert/byUserEntityGrant/index.json index a444281dd..91d2cedff 100644 --- a/es/components/userRelation/upsert/byUserEntityGrant/index.json +++ b/es/components/userRelation/upsert/byUserEntityGrant/index.json @@ -10,7 +10,10 @@ "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", "l-radio": "@oak-frontend-base/miniprogram_npm/lin-ui/radio/index", "l-radio-group": "@oak-frontend-base/miniprogram_npm/lin-ui/radio-group/index", + "l-checkbox-group": "@oak-frontend-base/miniprogram_npm/lin-ui/checkbox-group/index", + "l-checkbox": "@oak-frontend-base/miniprogram_npm/lin-ui/checkbox/index", "l-counter": "@oak-frontend-base/miniprogram_npm/lin-ui/counter/index", - "userEntityGrant-detail": "../../../../pages/userEntityGrant/detail/index" + "l-switch": "@oak-frontend-base/miniprogram_npm/lin-ui/switch/index", + "userEntityGrant-share": "../../../userEntityGrant/share/index" } } diff --git a/es/components/userRelation/upsert/byUserEntityGrant/index.xml b/es/components/userRelation/upsert/byUserEntityGrant/index.xml index 252f2f835..7881f3f5d 100644 --- a/es/components/userRelation/upsert/byUserEntityGrant/index.xml +++ b/es/components/userRelation/upsert/byUserEntityGrant/index.xml @@ -1,112 +1,68 @@ - - + + 请通过分享或者截屏二维码方式分享权限 - - - - - 权限: - - - - {{item.display || t(entity + ':r.' + item.name)}} - - + + - 人次: + {{t('userEntityGrant:attr.relationIds')}}: - - - 单人次 - - - 不限人次 - - + + + {{item.display || t(entity + ':r.' + item.name)}} + + - + + + + {{t('userEntityGrant:attr.rule')}}: + + + + {{t('userEntityGrant:v.rule.' + item)}} + + + + + + + + {{t('multiple')}}: + + + + + - 有效期: + {{t('userEntityGrant:attr.expiresAt')}}: - - + - + {{unitArr[unitIndex].label}} + + + - - - - - 确定 + + {{t('common::action.confirm')}} \ No newline at end of file diff --git a/es/components/userRelation/upsert/byUserEntityGrant/web.pc.js b/es/components/userRelation/upsert/byUserEntityGrant/web.pc.js index 07924415d..fd99da263 100644 --- a/es/components/userRelation/upsert/byUserEntityGrant/web.pc.js +++ b/es/components/userRelation/upsert/byUserEntityGrant/web.pc.js @@ -6,7 +6,7 @@ export default function render(props) { const { relations, userEntityGrant, userEntityGrantId, period, unit, maxes, oakExecutable, rules, } = props.data; const { relationIds, type, rule, multiple, relationEntity } = userEntityGrant || {}; const { update, t, onBack, confirm, setInit, setPeriod, setUnit } = props.methods; - const P = !!userEntityGrantId ? (_jsxs(_Fragment, { children: [_jsx(Alert, { showIcon: true, message: t('shareCode'), type: "info", style: { marginBottom: 16 } }), _jsx(UserEntityGrantShare, { showBack: false, oakId: userEntityGrantId, oakAutoUnmount: true, oakPath: "$userRelation/upsert/byUserEntityGrant-userEntityGrant/detail" }), _jsx("div", { style: { + const P = !!userEntityGrantId ? (_jsxs(_Fragment, { children: [_jsx(Alert, { showIcon: true, message: t('shareCode'), type: "info", style: { marginBottom: 16 } }), _jsx(UserEntityGrantShare, { oakId: userEntityGrantId, oakAutoUnmount: true, oakPath: "$userRelation/upsert/byUserEntityGrant-userEntityGrant/detail" }), _jsx("div", { style: { width: '100%', display: 'flex', justifyContent: 'flex-end', diff --git a/es/components/userRelation/upsert/onUser/index.xml b/es/components/userRelation/upsert/onUser/index.xml index 34dd0614a..20edd5ba4 100644 --- a/es/components/userRelation/upsert/onUser/index.xml +++ b/es/components/userRelation/upsert/onUser/index.xml @@ -2,7 +2,7 @@ 权限: - + diff --git a/es/components/userRelation/upsert/onUser/web.js b/es/components/userRelation/upsert/onUser/web.js index c9887a0c1..303fbe60e 100644 --- a/es/components/userRelation/upsert/onUser/web.js +++ b/es/components/userRelation/upsert/onUser/web.js @@ -28,7 +28,5 @@ export default function Render(props) { { required: true, }, - ], name: "relation", children: _jsx(UserRelation, { oakAutoUnmount: true, oakPath: oakFullpath - ? `${oakFullpath}.userRelation$user` - : undefined, entity: entity, entityId: entityId, relations: relations }) })] })); + ], name: "relation", children: _jsx(UserRelation, { oakAutoUnmount: true, oakPath: `${oakFullpath}.userRelation$user`, entity: entity, entityId: entityId, relations: relations }) })] })); } diff --git a/es/components/userRelation/upsert/onUser/web.pc.js b/es/components/userRelation/upsert/onUser/web.pc.js index f991620cc..8120d3bc2 100644 --- a/es/components/userRelation/upsert/onUser/web.pc.js +++ b/es/components/userRelation/upsert/onUser/web.pc.js @@ -96,7 +96,5 @@ export default function Render(props) { { required: true, }, - ], name: "relation", children: _jsx(UserRelation, { oakAutoUnmount: true, oakPath: oakFullpath - ? `${oakFullpath}.userRelation$user` - : undefined, entity: entity, entityId: entityId, relations: relations }) })] }) })); + ], name: "relation", children: _jsx(UserRelation, { oakAutoUnmount: true, oakPath: `${oakFullpath}.userRelation$user`, entity: entity, entityId: entityId, relations: relations }) })] }) })); } diff --git a/es/data/i18n.js b/es/data/i18n.js index 1eacf5703..886715ab8 100644 --- a/es/data/i18n.js +++ b/es/data/i18n.js @@ -1,20 +1,5 @@ // 本文件为自动编译产生,请勿直接修改 const i18ns = [ - { - id: "ae2826a9f7a8f93c6619f05a9dfe6215", - namespace: "oak-general-business-p-mobile-login", - language: "zh-CN", - module: "oak-general-business", - position: "src/pages/mobile/login", - data: { - "Login": "确定", - "Send": "发送验证码", - "placeholder": { - "Captcha": "输入4位短信验证码", - "Mobile": "请输入手机号" - } - } - }, { id: "a85ac2eff2d138152ef083e37c2b366c", namespace: "oak-general-business-p-paper-detail", @@ -67,17 +52,6 @@ const i18ns = [ } } }, - { - id: "2a0f8b18e08c3f7b82353d16b6ba4651", - namespace: "oak-general-business-p-token-me", - language: "zh-CN", - module: "oak-general-business", - position: "src/pages/token/me", - data: { - "login": "登录", - "syncWeChat": "同步微信信息" - } - }, { id: "b40594f173c7c593719746c2f63ca126", namespace: "oak-general-business-p-user-info", @@ -220,6 +194,21 @@ const i18ns = [ "dragSort": "当前拖拽排序值超过上限范围" } }, + { + id: "abfd81b3031c5a66f99fa1b2721d10fc", + namespace: "oak-general-business-c-mobile-login", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/mobile/login", + data: { + "Login": "确定", + "Send": "发送验证码", + "placeholder": { + "Captcha": "输入4位短信验证码", + "Mobile": "请输入手机号" + } + } + }, { id: "7ab2802526051f5926338d25cf7f3824", namespace: "oak-general-business-c-my-info", @@ -293,6 +282,40 @@ const i18ns = [ "application-list": "应用管理" } }, + { + id: "7bcbb4dbb525e9a575095102f673f7ba", + namespace: "oak-general-business-c-token-me", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/token/me", + data: { + "login": "登录", + "syncWeChat": "同步微信信息" + } + }, + { + id: "34e1363e6dc2a9b3a4e5defc0d570e38", + namespace: "oak-general-business-c-user-info", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/user/info", + data: { + "avatar": "头像", + "mobile": "手机号", + "password": "密码", + "manage": "管理", + "bind": "绑定", + "syncWeChat": "同步微信信息", + "send": "发送验证码", + "cancel": "取消", + "unbind": "解绑", + "Mobile-Number-Verification": "手机号验证", + "unbindingWechat": "确定解绑微信账号", + "placeholder": { + "Captcha": "输入4位短信验证码" + } + } + }, { id: "31967b6cdd8488887e7d2f39fe166458", namespace: "oak-general-business-c-user-login", @@ -314,6 +337,18 @@ const i18ns = [ "resendAfter": "秒后可重发" } }, + { + id: "c2d000183900d85963c1d18f0f872bf5", + namespace: "oak-general-business-c-user-manage-detail", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/user/manage/detail", + data: { + "mobile": "手机号", + "unset": "未设置", + "avatar": "头像" + } + }, { id: "5bf96a3e054b8d73c76d7bb45ea90a80", namespace: "oak-general-business-c-userEntityGrant-claim", diff --git a/es/pages/user/info/web.pc.js b/es/pages/user/info/web.pc.js index 729633d44..bf80a7974 100644 --- a/es/pages/user/info/web.pc.js +++ b/es/pages/user/info/web.pc.js @@ -5,7 +5,7 @@ import dayjs from 'dayjs'; import PageHeader from '../../../components/common/pageHeader'; import OakAvatar from '../../../components/extraFile/avatar'; import ExtraFileCommit from '../../../components/extraFile/commit'; -import MobileLogin from '../../../pages/mobile/login'; +import MobileLogin from '../../../components/mobile/login'; import WechatLoginQrCode from '../../../components/wechatLogin/qrCode'; import WechatUserList from '../../../components/wechatUser/bindingList'; import { isCaptcha, } from 'oak-domain/lib/utils/validator'; diff --git a/es/pages/user/manage/detail/index.js b/es/pages/user/manage/detail/index.js index 7e50364c5..8560a07c2 100644 --- a/es/pages/user/manage/detail/index.js +++ b/es/pages/user/manage/detail/index.js @@ -44,8 +44,9 @@ export default OakComponent({ refId: 1, }, isList: false, - formData: ({ data: user, features, legalActions }) => { + formData: ({ data: user, features }) => { const { id, nickname, idState, userState, name, gender, mobile$user, birth, extraFile$entity, } = user || {}; + const legalActions = user?.['#oakLegalActions'] || []; const mobile = mobile$user && mobile$user[0]?.mobile; const mobileCount = mobile$user ? mobile$user.length : 0; const mobileText = mobileCount && mobileCount > 1 diff --git a/es/pages/userEntityGrant/detail/web.d.ts b/es/pages/userEntityGrant/detail/web.d.ts index 058f6a823..ea179cba6 100644 --- a/es/pages/userEntityGrant/detail/web.d.ts +++ b/es/pages/userEntityGrant/detail/web.d.ts @@ -1,9 +1,6 @@ import { WebComponentProps } from 'oak-frontend-base'; import { EntityDict } from '../../../oak-app-domain'; export default function Render(props: WebComponentProps): import("react/jsx-runtime").JSX.Element; diff --git a/es/pages/userEntityGrant/detail/web.js b/es/pages/userEntityGrant/detail/web.js index fad81d7cc..eb4a62b36 100644 --- a/es/pages/userEntityGrant/detail/web.js +++ b/es/pages/userEntityGrant/detail/web.js @@ -1,20 +1,11 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime"; -import Style from './web.module.less'; -import PageHeader from '../../../components/common/pageHeader'; import QrCode from '../../../components/common/qrCode'; import { Spin } from 'antd'; export default function Render(props) { - const { variant, showBack = true, url, expiresAt, title = '授权二维码', oakLoading, } = props.data; - return (_jsx(Container, { showBack: showBack, variant: variant, title: title, children: oakLoading ? (_jsx("div", { style: { + const { url, expiresAt, oakLoading, } = props.data; + return (_jsx(_Fragment, { children: oakLoading ? (_jsx("div", { style: { display: 'flex', justifyContent: 'center', padding: '48px', }, children: _jsx(Spin, { size: "large" }) })) : (_jsx(QrCode, { url: url, expiresAt: expiresAt })) })); } -function Container(props) { - const { children, variant = 'alone', showBack, title, } = props; - if (['inline', 'dialog'].includes(variant)) { - return _jsx(_Fragment, { children: children }); - } - return (_jsx(PageHeader, { showBack: showBack, title: title, children: _jsx("div", { className: Style.container, children: children }) })); -} diff --git a/es/pages/userEntityGrant/detail/web.pc.d.ts b/es/pages/userEntityGrant/detail/web.pc.d.ts index 058f6a823..ea179cba6 100644 --- a/es/pages/userEntityGrant/detail/web.pc.d.ts +++ b/es/pages/userEntityGrant/detail/web.pc.d.ts @@ -1,9 +1,6 @@ import { WebComponentProps } from 'oak-frontend-base'; import { EntityDict } from '../../../oak-app-domain'; export default function Render(props: WebComponentProps): import("react/jsx-runtime").JSX.Element; diff --git a/es/pages/userEntityGrant/detail/web.pc.js b/es/pages/userEntityGrant/detail/web.pc.js index fad81d7cc..eb4a62b36 100644 --- a/es/pages/userEntityGrant/detail/web.pc.js +++ b/es/pages/userEntityGrant/detail/web.pc.js @@ -1,20 +1,11 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime"; -import Style from './web.module.less'; -import PageHeader from '../../../components/common/pageHeader'; import QrCode from '../../../components/common/qrCode'; import { Spin } from 'antd'; export default function Render(props) { - const { variant, showBack = true, url, expiresAt, title = '授权二维码', oakLoading, } = props.data; - return (_jsx(Container, { showBack: showBack, variant: variant, title: title, children: oakLoading ? (_jsx("div", { style: { + const { url, expiresAt, oakLoading, } = props.data; + return (_jsx(_Fragment, { children: oakLoading ? (_jsx("div", { style: { display: 'flex', justifyContent: 'center', padding: '48px', }, children: _jsx(Spin, { size: "large" }) })) : (_jsx(QrCode, { url: url, expiresAt: expiresAt })) })); } -function Container(props) { - const { children, variant = 'alone', showBack, title, } = props; - if (['inline', 'dialog'].includes(variant)) { - return _jsx(_Fragment, { children: children }); - } - return (_jsx(PageHeader, { showBack: showBack, title: title, children: _jsx("div", { className: Style.container, children: children }) })); -} diff --git a/es/triggers/userEntityGrant.js b/es/triggers/userEntityGrant.js index cdf24a133..12eb99608 100644 --- a/es/triggers/userEntityGrant.js +++ b/es/triggers/userEntityGrant.js @@ -11,7 +11,7 @@ const triggers = [ const fn = async (userEntityGrantData) => { const { userId } = context.getToken(); assert(userId); - const { id } = userEntityGrantData; + const { id, claimUrl } = userEntityGrantData; Object.assign(userEntityGrantData, { granterId: userId, expired: false, @@ -28,7 +28,7 @@ const triggers = [ data: { id: await generateNewIdAsync(), props: { - pathname: '/userEntityGrant/confirm', + pathname: claimUrl || '/userEntityGrant/claim', props: { oakId: id, }, diff --git a/lib/data/i18n.js b/lib/data/i18n.js index aba0a56c7..3affb8925 100644 --- a/lib/data/i18n.js +++ b/lib/data/i18n.js @@ -2,21 +2,6 @@ // 本文件为自动编译产生,请勿直接修改 Object.defineProperty(exports, "__esModule", { value: true }); const i18ns = [ - { - id: "ae2826a9f7a8f93c6619f05a9dfe6215", - namespace: "oak-general-business-p-mobile-login", - language: "zh-CN", - module: "oak-general-business", - position: "src/pages/mobile/login", - data: { - "Login": "确定", - "Send": "发送验证码", - "placeholder": { - "Captcha": "输入4位短信验证码", - "Mobile": "请输入手机号" - } - } - }, { id: "a85ac2eff2d138152ef083e37c2b366c", namespace: "oak-general-business-p-paper-detail", @@ -69,17 +54,6 @@ const i18ns = [ } } }, - { - id: "2a0f8b18e08c3f7b82353d16b6ba4651", - namespace: "oak-general-business-p-token-me", - language: "zh-CN", - module: "oak-general-business", - position: "src/pages/token/me", - data: { - "login": "登录", - "syncWeChat": "同步微信信息" - } - }, { id: "b40594f173c7c593719746c2f63ca126", namespace: "oak-general-business-p-user-info", @@ -222,6 +196,21 @@ const i18ns = [ "dragSort": "当前拖拽排序值超过上限范围" } }, + { + id: "abfd81b3031c5a66f99fa1b2721d10fc", + namespace: "oak-general-business-c-mobile-login", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/mobile/login", + data: { + "Login": "确定", + "Send": "发送验证码", + "placeholder": { + "Captcha": "输入4位短信验证码", + "Mobile": "请输入手机号" + } + } + }, { id: "7ab2802526051f5926338d25cf7f3824", namespace: "oak-general-business-c-my-info", @@ -295,6 +284,40 @@ const i18ns = [ "application-list": "应用管理" } }, + { + id: "7bcbb4dbb525e9a575095102f673f7ba", + namespace: "oak-general-business-c-token-me", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/token/me", + data: { + "login": "登录", + "syncWeChat": "同步微信信息" + } + }, + { + id: "34e1363e6dc2a9b3a4e5defc0d570e38", + namespace: "oak-general-business-c-user-info", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/user/info", + data: { + "avatar": "头像", + "mobile": "手机号", + "password": "密码", + "manage": "管理", + "bind": "绑定", + "syncWeChat": "同步微信信息", + "send": "发送验证码", + "cancel": "取消", + "unbind": "解绑", + "Mobile-Number-Verification": "手机号验证", + "unbindingWechat": "确定解绑微信账号", + "placeholder": { + "Captcha": "输入4位短信验证码" + } + } + }, { id: "31967b6cdd8488887e7d2f39fe166458", namespace: "oak-general-business-c-user-login", @@ -316,6 +339,18 @@ const i18ns = [ "resendAfter": "秒后可重发" } }, + { + id: "c2d000183900d85963c1d18f0f872bf5", + namespace: "oak-general-business-c-user-manage-detail", + language: "zh-CN", + module: "oak-general-business", + position: "src/components/user/manage/detail", + data: { + "mobile": "手机号", + "unset": "未设置", + "avatar": "头像" + } + }, { id: "5bf96a3e054b8d73c76d7bb45ea90a80", namespace: "oak-general-business-c-userEntityGrant-claim", diff --git a/lib/triggers/userEntityGrant.js b/lib/triggers/userEntityGrant.js index 35efd7500..a8b6c61e2 100644 --- a/lib/triggers/userEntityGrant.js +++ b/lib/triggers/userEntityGrant.js @@ -13,7 +13,7 @@ const triggers = [ const fn = async (userEntityGrantData) => { const { userId } = context.getToken(); (0, assert_1.assert)(userId); - const { id } = userEntityGrantData; + const { id, claimUrl } = userEntityGrantData; Object.assign(userEntityGrantData, { granterId: userId, expired: false, @@ -30,7 +30,7 @@ const triggers = [ data: { id: await (0, uuid_1.generateNewIdAsync)(), props: { - pathname: '/userEntityGrant/confirm', + pathname: claimUrl || '/userEntityGrant/claim', props: { oakId: id, }, diff --git a/package.json b/package.json index af07a6ca7..b42ac8886 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "copy-xml": "copyfiles -u 1 src/**/*.xml es/", "copy-wxml": "copyfiles -u 1 src/**/*.wxml es/", "prebuild": "npm run make:locale", - "build": "tsc -p tsconfig.json && tsc -p tsconfig.es.json && npm run copy-js && npm run copy-less && npm run copy-wxs && npm run copy-svg && npm run copy-xml && npm run copy-wxml", + "build": "tsc -p tsconfig.lib.json && tsc -p tsconfig.es.json && npm run copy-js && npm run copy-less && npm run copy-wxs && npm run copy-svg && npm run copy-xml && npm run copy-wxml", "gen:areaDebug": "ts-node ./scripts/generateAreaDebug.ts", "gen:subway": "ts-node ./scripts/local/getAmapSubway.ts", "clean:dir": "ts-node ./scripts/cleanDtsAndJs", diff --git a/src/components/common/tabBar/index.less b/src/components/common/tabBar/index.less index 820ddc782..b12388bc3 100644 --- a/src/components/common/tabBar/index.less +++ b/src/components/common/tabBar/index.less @@ -13,7 +13,7 @@ z-index: 999; width: 750rpx; - height: calc(106rpx + env(safe-area-inset-bottom)); + height: 106rpx; display: flex; background-color: white; background-size: 100% 100%; diff --git a/src/components/mobile/login/index.json b/src/components/mobile/login/index.json new file mode 100644 index 000000000..0acccd206 --- /dev/null +++ b/src/components/mobile/login/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "绑定手机号", + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-card": "@oak-frontend-base/miniprogram_npm/lin-ui/card/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-dialog": "@oak-frontend-base/miniprogram_npm/lin-ui/dialog/index" + } +} diff --git a/src/components/mobile/login/index.less b/src/components/mobile/login/index.less new file mode 100644 index 000000000..b30d31b4d --- /dev/null +++ b/src/components/mobile/login/index.less @@ -0,0 +1,40 @@ + +/** index.wxss **/ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + justify-content: center; + align-items: center; + box-sizing: border-box; + background-color: @oak-bg-color-page; + .safe-area-inset-bottom(); +} + +.container { + flex: 1; + display: flex; + flex-direction: column; +} + +.container2 { + flex-direction: row; + align-items: center; + justify-content: center; +} + +.card { + min-width: 480rpx; + text-align: center; +} + +.list { + background-color: #fff; + margin-top: 20rpx; + padding: 0 20rpx; + +} \ No newline at end of file diff --git a/src/pages/mobile/login/index.ts b/src/components/mobile/login/index.ts similarity index 100% rename from src/pages/mobile/login/index.ts rename to src/components/mobile/login/index.ts diff --git a/src/components/mobile/login/index.xml b/src/components/mobile/login/index.xml new file mode 100644 index 000000000..ef09f075d --- /dev/null +++ b/src/components/mobile/login/index.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + 您尚未授权手机号 + + + + + + 授权手机号 + + + diff --git a/src/components/mobile/login/locales/zh_CN.json b/src/components/mobile/login/locales/zh_CN.json new file mode 100644 index 000000000..9015124a9 --- /dev/null +++ b/src/components/mobile/login/locales/zh_CN.json @@ -0,0 +1,8 @@ +{ + "Login": "确定", + "Send": "发送验证码", + "placeholder": { + "Captcha": "输入4位短信验证码", + "Mobile": "请输入手机号" + } +} diff --git a/src/components/mobile/login/mobile.module.less b/src/components/mobile/login/mobile.module.less new file mode 100644 index 000000000..2ad9229fc --- /dev/null +++ b/src/components/mobile/login/mobile.module.less @@ -0,0 +1,35 @@ + + +.loginbox-main { + height: 100vh; + display: flex; + flex: 1; + align-items: center; + flex-direction: column; + background: var(--oak-bg-color-container); + padding-top: 20%; +} + +.loginbox-wrap { + width: 90%; + display: block; + background: var(--oak-bg-color-container); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 0 4px rgb(0 0 0 / 8%); +} + +.loginbox-hd { + padding: 32px; + font-size: 14px; + font-weight: 500; +} + +.loginbox-bd { + height: 200px; +} + +.loginbox-mobile { + position: relative; + padding: 0 32px; +} diff --git a/src/components/mobile/login/web.module.less b/src/components/mobile/login/web.module.less new file mode 100644 index 000000000..200e4c61b --- /dev/null +++ b/src/components/mobile/login/web.module.less @@ -0,0 +1,37 @@ + + +.loginbox-main { + display: flex; + flex: 1; + align-items: center; + flex-direction: column; + justify-content: center; + background: var(--oak-bg-color-container); +} + +.loginbox-wrap { + width: 400px; + display: block; + background: var(--oak-bg-color-container); + border-radius: 4px; + overflow: hidden; + box-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 0 4px rgb(0 0 0 / 8%); +} + +.loginbox-hd { + padding: 32px; + font-size: 14px; + font-weight: 500; +} + +.loginbox-bd { + height: 200px; +} + +.loginbox-mobile { + position: relative; + padding: 0 32px; +} + + + diff --git a/src/pages/mobile/login/web.pc.tsx b/src/components/mobile/login/web.pc.tsx similarity index 100% rename from src/pages/mobile/login/web.pc.tsx rename to src/components/mobile/login/web.pc.tsx diff --git a/src/pages/mobile/login/web.tsx b/src/components/mobile/login/web.tsx similarity index 100% rename from src/pages/mobile/login/web.tsx rename to src/components/mobile/login/web.tsx diff --git a/src/components/mobile/me/index.json b/src/components/mobile/me/index.json new file mode 100644 index 000000000..0acccd206 --- /dev/null +++ b/src/components/mobile/me/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "绑定手机号", + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-card": "@oak-frontend-base/miniprogram_npm/lin-ui/card/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-dialog": "@oak-frontend-base/miniprogram_npm/lin-ui/dialog/index" + } +} diff --git a/src/components/mobile/me/index.less b/src/components/mobile/me/index.less new file mode 100644 index 000000000..322711218 --- /dev/null +++ b/src/components/mobile/me/index.less @@ -0,0 +1,38 @@ +/** index.wxss **/ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + box-sizing: border-box; + align-items: stretch; + background-color: @oak-bg-color-page; + .safe-area-inset-bottom(); +} + +.container { + flex: 1; + display: flex; + flex-direction: column; +} + +.container2 { + flex-direction: row; + align-items: center; + justify-content: center; +} + +.card { + min-width: 480rpx; + text-align: center; +} + +.list { + background-color: #fff; + margin-top: 20rpx; + padding: 0 20rpx; + +} \ No newline at end of file diff --git a/src/pages/mobile/me/index.ts b/src/components/mobile/me/index.ts similarity index 100% rename from src/pages/mobile/me/index.ts rename to src/components/mobile/me/index.ts diff --git a/src/components/mobile/me/index.xml b/src/components/mobile/me/index.xml new file mode 100644 index 000000000..1a9d50b77 --- /dev/null +++ b/src/components/mobile/me/index.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + 您尚未授权手机号 + + + + + + + 授权手机号 + + + + diff --git a/src/components/mobile/me/mobile.module.less b/src/components/mobile/me/mobile.module.less new file mode 100644 index 000000000..1cb437d13 --- /dev/null +++ b/src/components/mobile/me/mobile.module.less @@ -0,0 +1,33 @@ + + +.container { + height: 100vh; + display: flex; + flex-direction: column; +} + +.list { + :global { + .t-list-item__meta { + + &-avatar { + width: auto !important; + height: auto !important; + background: transparent; + border-radius: unset; + } + &-title { + margin: 0 !important; + + } + } + } +} + +.noData { + display: flex; + flex-direction: column; + flex: 1; + align-items: center; + justify-content: center; +} \ No newline at end of file diff --git a/src/components/mobile/me/web.pc.tsx b/src/components/mobile/me/web.pc.tsx new file mode 100644 index 000000000..eaaf0c328 --- /dev/null +++ b/src/components/mobile/me/web.pc.tsx @@ -0,0 +1,107 @@ +import React, { useState } from 'react'; +import { List, Button, Modal, Row, Col } from 'antd'; +import { MobileOutlined, DeleteOutlined } from '@ant-design/icons'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +import MobileLogin from '../login'; + + +export default function render( + props: WebComponentProps< + EntityDict, + 'mobile', + true, + { + mobiles?: EntityDict['mobile']['OpSchema'][]; + allowRemove: boolean; + showBack: boolean; + tokenMobileId?: string; + }, + { + goAddMobile: () => void; + } + > +) { + const { mobiles, allowRemove, tokenMobileId, showBack = false } = props.data; + const { goAddMobile, removeItem, recoverItem, execute, subEvent } = props.methods; + const [open, setOpen] = useState(false); + const eventLoggedIn = `user:info:login:${Date.now()}`; + + return ( + <> + + + + + {mobiles?.map((ele, index) => ( + { + const modal = Modal!.confirm!({ + title: `确认删除吗?删除后无法用此号码登录`, + okText: '确定', + cancelText: '取消', + onOk: async (e) => { + removeItem(ele.id); + try { + await execute(); + } catch (err) { + recoverItem(ele.id); + throw err; + } + modal!.destroy!(); + }, + onCancel: (e) => { + modal!.destroy!(); + }, + }); + }} + > + + + ) + } + > + } + title={ele.mobile} + > + + ))} + + + + { + setOpen(false); + }} + > +
+ { + setOpen(false); + }} + oakPath="$mobile/me-mobile/login" + oakAutoUnmount={true} + /> +
+
+ + ); +} diff --git a/src/pages/mobile/me/web.tsx b/src/components/mobile/me/web.tsx similarity index 100% rename from src/pages/mobile/me/web.tsx rename to src/components/mobile/me/web.tsx diff --git a/src/components/token/me/index.json b/src/components/token/me/index.json new file mode 100644 index 000000000..a9bf8da87 --- /dev/null +++ b/src/components/token/me/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "个人中心", + "usingComponents": { + "oak-icon": "../../../components/icon/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index" + } +} diff --git a/src/components/token/me/index.less b/src/components/token/me/index.less new file mode 100644 index 000000000..a8778d413 --- /dev/null +++ b/src/components/token/me/index.less @@ -0,0 +1,31 @@ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100%; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + // background-image: linear-gradient(to bottom, #ffffff, #f4f5f6); +} + +.userInfo { + padding: 14rpx; + display: flex; + flex-direction: column; + align-items: center; +} + +.nickname { + font-size: 30rpx; + color: @oak-text-color-secondary; + padding-top: 14rpx; + padding-bottom: 14rpx; +} + + +.list { + background-color: #ffffff; + padding: 0 10rpx; +} diff --git a/src/pages/token/me/index.ts b/src/components/token/me/index.ts similarity index 100% rename from src/pages/token/me/index.ts rename to src/components/token/me/index.ts diff --git a/src/components/token/me/index.xml b/src/components/token/me/index.xml new file mode 100644 index 000000000..0d304a089 --- /dev/null +++ b/src/components/token/me/index.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + {{t('login')}} + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/token/me/locales/zh_CN.json b/src/components/token/me/locales/zh_CN.json new file mode 100644 index 000000000..d4ac5f383 --- /dev/null +++ b/src/components/token/me/locales/zh_CN.json @@ -0,0 +1,4 @@ +{ + "login": "登录", + "syncWeChat": "同步微信信息" +} diff --git a/src/components/token/me/mobile.module.less b/src/components/token/me/mobile.module.less new file mode 100644 index 000000000..228807928 --- /dev/null +++ b/src/components/token/me/mobile.module.less @@ -0,0 +1,40 @@ +.container { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-page); +} + +.userInfo { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px 10px 10px 10px; + margin-bottom: 10px; + background-color: var(--oak-bg-color-container); + + .avatar { + width: 60px; + height: 60px; + margin-bottom: 10px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .nickname { + margin-bottom: 10px; + font-size: 14px; + } + + .userIcon { + color: #fff; + font-size: 24px; + } +} + +.list { + background-color: #fff; +} \ No newline at end of file diff --git a/src/components/token/me/web.module.less b/src/components/token/me/web.module.less new file mode 100644 index 000000000..55cc5ed57 --- /dev/null +++ b/src/components/token/me/web.module.less @@ -0,0 +1,61 @@ +.container { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-page); +} + +.userInfo { + display: flex; + flex-direction: column; + align-items: center; + padding: 20px 10px 10px 10px; + margin-bottom: 10px; + background-color: var(--oak-bg-color-container); + + .avatar { + width: 60px; + height: 60px; + margin-bottom: 10px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .nickname { + margin-bottom: 10px; + font-size: 14px; + } + + .userIcon { + color: #fff; + font-size: 24px; + } +} + +.list { + background-color: #fff; + padding: 0 10px; + + :global { + .t-list-item__meta { + + &-avatar { + width: auto !important; + height: auto !important; + background: transparent; + border-radius: unset; + display: flex; + justify-content: center; + align-items: center; + } + + &-title { + margin: 0 !important; + + } + } + } +} \ No newline at end of file diff --git a/src/pages/token/me/web.pc.tsx b/src/components/token/me/web.pc.tsx similarity index 100% rename from src/pages/token/me/web.pc.tsx rename to src/components/token/me/web.pc.tsx diff --git a/src/pages/token/me/web.tsx b/src/components/token/me/web.tsx similarity index 100% rename from src/pages/token/me/web.tsx rename to src/components/token/me/web.tsx diff --git a/src/components/user/info/index.json b/src/components/user/info/index.json new file mode 100644 index 000000000..7b1beae1c --- /dev/null +++ b/src/components/user/info/index.json @@ -0,0 +1,17 @@ +{ + "navigationBarTitleText": "个人设置", + "enablePullDownRefresh": false, + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-tag": "@oak-frontend-base/miniprogram_npm/lin-ui/tag/index", + "l-popup": "@oak-frontend-base/miniprogram_npm/lin-ui/popup/index", + "l-input": "@oak-frontend-base/miniprogram_npm/lin-ui/input/index", + "l-radio-group": "@oak-frontend-base/miniprogram_npm/lin-ui/radio-group/index", + "l-radio": "@oak-frontend-base/miniprogram_npm/lin-ui/radio/index", + "l-form-item": "@oak-frontend-base/miniprogram_npm/lin-ui/form-item/index", + "oak-extraFile-avatar": "../../../components/extraFile/avatar/index" + } +} diff --git a/src/components/user/info/index.less b/src/components/user/info/index.less new file mode 100644 index 000000000..cfa36719d --- /dev/null +++ b/src/components/user/info/index.less @@ -0,0 +1,90 @@ +/** index.wxss **/ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.avatar { + margin: 10rpx 0; +} + + +.col { + margin-top: 20rpx; + display: flex; + flex-direction: column; + padding: 0rpx 10rpx; + background-color: #fff; + + .left { + display: flex; + flex-direction: row; + } + + .icon { + width: 16rpx; + margin-left: 6rpx; + margin-right: 26rpx; + text-align: center; + } + + .label { + color: @oak-text-color-primary; + } + + .value { + color: @oak-text-color-secondary; + margin-right: 16rpx; + + .primary { + background-color: @oak-color-primary; + } + + .success { + background-color: @oak-color-success; + } + + .danger { + background-color: @oak-color-error; + } + + .warning { + background-color: @oak-color-warning; + } + } +} + + +.pupop-content { + display: flex; + flex-direction: column; + background-color: #fff; + min-height: 300rpx; + .safe-area-inset-bottom(); + + .pupop-header { + padding: 20rpx; + display: flex; + flex-direction: column; + align-items: flex-end; + + + .close { + display: block; + } + } + + .pupop-form { + display: flex; + flex-direction: column; + flex: 1; + } +} \ No newline at end of file diff --git a/src/components/user/info/index.ts b/src/components/user/info/index.ts new file mode 100644 index 000000000..93e28ce97 --- /dev/null +++ b/src/components/user/info/index.ts @@ -0,0 +1,376 @@ +import { OakUserInvisibleException } from 'oak-domain/lib/types'; +import dayjs from 'dayjs'; +import { LOCAL_STORAGE_KEYS } from '../../../config/constants'; + +type Attr = 'nickname' | 'gender' | 'birth'; +const SEND_KEY = LOCAL_STORAGE_KEYS.captchaSendAt; +const SEND_CAPTCHA_LATENCY = process.env.NODE_ENV === 'development' ? 10 : 60; + +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + name: 1, + nickname: 1, + birth: 1, + gender: 1, + idState: 1, + userState: 1, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + userId: 1, + user: { + id: 1, + userState: 1, + refId: 1, + }, + }, + }, + user$ref: { + $entity: 'user', + data: { + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + userId: 1, + user: { + id: 1, + userState: 1, + refId: 1, + }, + }, + }, + }, + }, + extraFile$entity: { + $entity: 'extraFile', + data: { + id: 1, + tag1: 1, + origin: 1, + bucket: 1, + objectId: 1, + filename: 1, + extra1: 1, + extension: 1, + type: 1, + entity: 1, + entityId: 1, + }, + filter: { + tag1: 'avatar', + }, + indexFrom: 0, + count: 1, + }, + wechatUser$user: { + $entity: 'wechatUser', + data: { + id: 1, + openId: 1, + unionId: 1, + userId: 1, + origin: 1, + nickname: 1, + user: { + id: 1, + userState: 1, + refId: 1, + }, + }, + }, + }, + isList: false, + formData({ data: user, features }) { + const avatar = user?.extraFile$entity && user?.extraFile$entity[0]; + const avatarUrl = features.extraFile.getUrl(avatar); + const { mobile } = + (user?.mobile$user && user?.mobile$user[0]) || + (user?.user$ref && + user?.user$ref[0] && + user?.user$ref[0].mobile$user && + user?.user$ref[0].mobile$user[0]) || + {}; + + const genderOption = + user?.gender && + this.state.genderOptions.find((ele) => ele.value === user?.gender); + + const lastSendAt = features.localStorage.load(SEND_KEY); + const now = Date.now(); + let counter = 0; + if (typeof lastSendAt === 'number') { + counter = Math.max( + SEND_CAPTCHA_LATENCY - Math.ceil((now - lastSendAt) / 1000), + 0 + ); + if (counter > 0) { + (this as any).counterHandler = setTimeout( + () => this.reRender(), + 1000 + ); + } else if ((this as any).counterHandler) { + clearTimeout((this as any).counterHandler); + (this as any).counterHandler = undefined; + } + } + const isRoot = features.token.isReallyRoot(); + + return { + id: user?.id, + name: user?.name, + nickname: user?.nickname, + gender: user?.gender, + genderStr: genderOption?.label, + birthText: user?.birth + ? dayjs(user.birth).format('YYYY-MM-DD') + : '', + birth: user?.birth, + avatarUrl, + mobile, + userState: user?.userState, + idState: user?.idState, + + wechatUser: user?.wechatUser$user?.[0], + counter, + isRoot, + }; + }, + data: { + stateColor: { + shadow: 'primary', + normal: 'success', + disabled: 'danger', + }, + idStateColor: { + verifying: 'primary', + verified: 'success', + unverified: 'warning', + }, + genderOptions: [ + { + value: 'male', + label: '男', + }, + { + value: 'female', + label: '女', + }, + ], + visible: false, + attr: '' as Attr | '', + attrs: { + nickname: '昵称', + gender: '性别', + birth: '出生日期', + }, + birthEnd: '', + refreshing: false, + }, + lifetimes: { + async ready() { + const { oakId } = this.props; + const userId = this.features.token.getUserId(); + if (userId !== oakId) { + throw new OakUserInvisibleException(); + } + this.setState({ birthEnd: dayjs().format('YYYY-MM-DD') }); + }, + }, + methods: { + async refreshWechatPublicUserInfo() { + this.setState({ + refreshing: true, + }); + try { + await this.features.token.refreshWechatPublicUserInfo(); + this.setState({ + refreshing: false, + }); + } catch (err) { + this.setState({ + refreshing: false, + }); + throw err; + } + }, + goUserManage() { + this.navigateTo({ + url: '/user/manage', + }); + }, + goAddMobile() { + this.navigateTo( + { + url: '/mobile/me', + }, + { + showBack: true, + } + ); + }, + goChangePassword() { + this.navigateTo( + { + url: '/changePassword', + }, + { + showBack: true, + } + ); + }, + setVisibleMp(e: WechatMiniprogram.Touch) { + const { + target: { dataset }, + } = e; + const { attr } = dataset!; + this.setVisible(true, attr); + }, + genderChangeMp(e: WechatMiniprogram.Touch) { + const { detail } = e; + const { checked, currentKey } = detail; + const { attr } = this.state; + this.setCustomData(attr as Attr, currentKey); + }, + birthChangeMp(e: WechatMiniprogram.Input) { + const { + detail: { value }, + } = e; + const birth = dayjs(dayjs(value).format('YYYY-MM-DD')).valueOf(); + const { attr } = this.state; + this.setState({ + birthText2: dayjs(value).format('YYYY-MM-DD'), + }); + this.setCustomData(attr as Attr, birth); + }, + setVisible(visible: boolean, attr: Attr) { + this.setState({ + visible, + attr: visible ? attr : '', + [`new_${attr}`]: '', + birthText2: '', + }); + }, + setCustomData(attr: Attr, value: string | number) { + this.setState({ + [`new_${attr}`]: value, + }); + }, + setCustomDataMp(e: WechatMiniprogram.Input) { + const { + detail, + target: { dataset }, + } = e; + const { value } = detail; + const { attr } = this.state; + + this.setCustomData(attr as Attr, value); + }, + updateData(attr: Attr, value: string | number) { + this.update({ + [attr]: this.state[`new_${attr}` as Attr], + }); + }, + async onConfirmMp() { + const { attr } = this.state; + await this.onConfirm(attr as Attr); + }, + async onConfirm(attr: Attr) { + const { oakId } = this.props; + if (!this.state[`new_${attr}` as Attr]) { + this.setMessage({ + type: 'warning', + content: `${this.state.attrs[attr]}不能为空`, + }); + return; + } + this.update({ + [attr]: this.state[`new_${attr}` as Attr], + }); + await this.execute(); + this.setVisible(false, attr as Attr); + }, + onPupopCloseMp() { + const { attr } = this.state; + this.clean(); + this.setVisible(false, attr as Attr); + }, + async updateMyInfo() { + if (!this.state.name) { + this.setMessage({ + type: 'error', + content: '请输入姓名', + }); + return; + } + if (!this.state.nickname) { + this.setMessage({ + type: 'error', + content: '请输入昵称', + }); + return; + } + // if (!this.state.gender) { + // this.setMessage({ + // type: 'error', + // content: '请选择性别', + // }); + // return; + // } + // if (!this.state.birth) { + // this.setMessage({ + // type: 'error', + // content: '请选择生日', + // }); + // return; + // } + await this.execute('update'); + }, + async sendCaptcha() { + const { mobile } = this.state; + try { + const result = await this.features.token.sendCaptcha( + mobile, + 'login' + ); + // 显示返回消息 + this.setMessage({ + type: 'success', + content: result, + }); + this.save(SEND_KEY, Date.now()); + this.reRender(); + } catch (err) { + this.setMessage({ + type: 'error', + content: (err as Error).message, + }); + } + }, + async unbindingWechat(captcha?: string) { + const { mobile, wechatUser } = this.state; + try { + await this.features.cache.exec('unbindingWechat', { + wechatUserId: wechatUser.id, + mobile, + captcha, + }); + this.refresh(); + this.setMessage({ + content: '解绑成功', + type: 'success', + }); + } catch (err) { + this.setMessage({ + content: '解绑失败', + type: 'warning', + }); + } + }, + }, +}); diff --git a/src/components/user/info/index.xml b/src/components/user/info/index.xml new file mode 100644 index 000000000..a824eb1cc --- /dev/null +++ b/src/components/user/info/index.xml @@ -0,0 +1,82 @@ + + + + + + + + + {{nickname || '未设置'}} + + + {{name || '未设置'}} + + + {{genderStr || '未设置'}} + + + {{birthText || '未设置'}} + + + {{mobile || '未绑定'}} + + + + + + {{t('user:v.idState.' + idState) || '未设置'}} + + + + + + + + + + + + + + + + + + + + + + + + + {{item.label}} + + + + + + + + + + + + + + + + + + + + 提交 + + + + \ No newline at end of file diff --git a/src/components/user/info/locales/zh_CN.json b/src/components/user/info/locales/zh_CN.json new file mode 100644 index 000000000..ad4bcd140 --- /dev/null +++ b/src/components/user/info/locales/zh_CN.json @@ -0,0 +1,16 @@ +{ + "avatar": "头像", + "mobile": "手机号", + "password": "密码", + "manage": "管理", + "bind": "绑定", + "syncWeChat": "同步微信信息", + "send": "发送验证码", + "cancel": "取消", + "unbind": "解绑", + "Mobile-Number-Verification": "手机号验证", + "unbindingWechat": "确定解绑微信账号", + "placeholder": { + "Captcha": "输入4位短信验证码" + } +} \ No newline at end of file diff --git a/src/components/user/info/mobile.module.less b/src/components/user/info/mobile.module.less new file mode 100644 index 000000000..17a0a95d6 --- /dev/null +++ b/src/components/user/info/mobile.module.less @@ -0,0 +1,38 @@ +.container { + background: var(--oak-bg-color-page); + display: flex; + flex-direction: column; + flex: 1; + height: 100vh; +} + +.syncWeChat { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin-top: 10px; +} + +.avatar_container { + height: 220px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + .avatar { + width: 84px; + height: 84px; + border-radius: 42px; + margin-bottom: 10px; + } +} + + +.list { + flex: 1; + margin-top: 10px; + margin-bottom: 10px; + background: var(--oak-bg-color-container); +} diff --git a/src/components/user/info/web.module.less b/src/components/user/info/web.module.less new file mode 100644 index 000000000..c9848913a --- /dev/null +++ b/src/components/user/info/web.module.less @@ -0,0 +1,7 @@ + +.container { + background: var(--oak-bg-color-container); + box-shadow: 0 2px 3px #0000001a; + border-radius: 3px; + padding: 30px 32px; +} diff --git a/src/components/user/info/web.pc.tsx b/src/components/user/info/web.pc.tsx new file mode 100644 index 000000000..e1aaa8036 --- /dev/null +++ b/src/components/user/info/web.pc.tsx @@ -0,0 +1,402 @@ +import React, { useState } from 'react'; +import { + Avatar, + Space, + Button, + Input, + Radio, + DatePicker, + Form, + Typography, + Modal, + Descriptions +} from 'antd'; +import dayjs from 'dayjs'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +import PageHeader from '../../../components/common/pageHeader'; +import OakAvatar from '../../../components/extraFile/avatar'; +import ExtraFileCommit from '../../../components/extraFile/commit'; +import MobileLogin from '../../../components/mobile/login'; +import WechatLoginQrCode from '../../../components/wechatLogin/qrCode'; +import WechatUserList from '../../../components/wechatUser/bindingList'; +import { + isCaptcha, +} from 'oak-domain/lib/utils/validator'; + +import Style from './web.module.less'; + + +export default function Render( + props: WebComponentProps< + EntityDict, + 'user', + false, + { + visible: boolean; + nickname: string; + name: string; + birth: string; + gender: string; + mobile: string; + avatarUrl: string; + attr: string; + showBack: boolean; + genderOptions: Array<{ label: string; value: string }>; + wechatUser: EntityDict['wechatUser']['Schema']; + counter: number; + isRoot: boolean; + }, + { + goUserManage: () => void; + goAddMobile: () => void; + sendCaptcha: () => void; + goChangePassword: () => void; + updateMyInfo: () => void; + unbindingWechat: (captcha?: string) => void; + } + > +) { + const { data, methods } = props; + const { + t, + updateMyInfo, + goAddMobile, + sendCaptcha, + unbindingWechat, + goChangePassword, + goUserManage, + } = methods; + const { + nickname, + name, + birth, + gender, + mobile, + avatarUrl, + showBack, + oakExecuting, + genderOptions, + oakFullpath, + oakDirty, + wechatUser, + counter, + isRoot, + } = data; + const [open, setOpen] = useState(false); + const [open2, setOpen2] = useState(false); + const [open3, setOpen3] = useState(false); + const [captcha, setCaptcha] = useState(''); + + return ( + +
+ +
+ + <> + + + + + <> + + methods.update({ + name: e.target.value, + }) + } + value={name} + /> + + + + <> + + methods.update({ + nickname: e.target.value, + }) + } + value={nickname} + /> + + + + + { + methods.update({ gender: value }); + }} + /> + + + + <> + { + if ( + dayjs(current).valueOf() > + dayjs().valueOf() + ) { + return true; + } + return false; + }} + onChange={(value) => { + if (value) { + methods.update({ + birth: dayjs(value).valueOf(), + }); + } + }} + /> + + + + + {/* */} + + + +
+
+
+
+ +
+ + + {mobile || '未设置'} + + + + + + {'********'} + + + + {process.env.NODE_ENV === 'development' && ( + + <> + {wechatUser ? ( + + + {wechatUser.nickname} + + + + ) : ( + + )} + + + )} + {isRoot && ( + + + + )} +
+
+ { + setOpen(false); + }} + > + { + setOpen(false); + }} + oakPath="$user/info-mobile/login" + oakAutoUnmount={true} + /> + + + { + setOpen2(false); + }} + > + + + setOpen3(false)}> + {t('cancel')} + , + , + ]} + maskClosable={false} + onCancel={() => { + setOpen3(false); + }} + > + + + 请输入 + {mobile && + mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')} + 收到的验证码 + + + { + setCaptcha(e.target.value); + }} + className={Style['loginbox-input']} + suffix={ + + } + /> + + + +
+ ); +} diff --git a/src/components/user/info/web.tsx b/src/components/user/info/web.tsx new file mode 100644 index 000000000..b3708e82f --- /dev/null +++ b/src/components/user/info/web.tsx @@ -0,0 +1,256 @@ +import React, { RefObject } from 'react'; +import { + List, + DatePicker, + Avatar, + Popup, + Form, + Button, + Input, + Radio, + Space, +} from 'antd-mobile'; +import type { DatePickerRef } from 'antd-mobile/es/components/date-picker'; +import dayjs from 'dayjs'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +import OakAvatar from '../../../components/extraFile/avatar'; +import Style from './mobile.module.less'; + + +type DataProps = { + visible: boolean; + nickname: string; + name: string; + birth: string; + gender: string; + mobile: string; + avatarUrl: string; + attr: string; + genderOptions: Array<{ label: string; value: string }>; + attrs: Record; + id: string; + refreshing: boolean; + isSupportSyncWeChat: boolean; + appId: string; +}; + +type MethodsProps = { + goAddMobile: () => void; + goChangePassword: () => void; + setAvatar: () => void; + setVisible: (visible: boolean, attr: string) => void; + setCustomData: (attr: string, value: string | number) => void; + onConfirm: (attr: string) => Promise; + refreshWechatPublicUserInfo: () => void; +}; + +export default function render( + props: WebComponentProps +) { + const { data, methods } = props; + const { + t, + clean, + setAvatar, + setVisible, + goAddMobile, + refreshWechatPublicUserInfo, + goChangePassword, + } = methods; + const { + oakFullpath, + visible, + nickname, + name, + birth, + gender, + mobile, + avatarUrl, + attr, + id, + isSupportSyncWeChat, + refreshing, + } = data; + + return ( +
+ + + +
+ } + > + 头像 + + { + setVisible(true, 'nickname'); + }} + > + {t('user:attr.nickname')} + + { + setVisible(true, 'gender'); + }} + > + {t('user:attr.gender')} + + { + setVisible(true, 'birth'); + }} + > + {t('user:attr.birth')} + + { + goAddMobile(); + }} + > + {t('mobile')} + + { + goChangePassword(); + }} + > + {t('password')} + + + + { + clean(); + setVisible(false, attr); + }} + bodyStyle={{ + borderTopLeftRadius: '8px', + borderTopRightRadius: '8px', + minHeight: '20vh', + }} + > + + + + ); +} + +function AttrUpsert(props: { data: DataProps; methods: MethodsProps }) { + const { data, methods } = props; + const { attr, genderOptions, attrs } = data; + + const { setCustomData, onConfirm, setVisible } = methods; + + const label = attrs[attr as keyof typeof attrs]; + + return ( +
+
{ + await onConfirm(attr); + }} + > + 提交 + + } + > + 修改信息 + + {attr === 'nickname' && ( + + { + setCustomData(attr, value); + }} + /> + + )} + {attr === 'gender' && ( + + { + setCustomData(attr, value); + }} + > + + {genderOptions.map( + (ele: { value: string; label: string }) => ( + + {ele.label} + + ) + )} + + + + )} + {attr === 'birth' && ( + + ) => { + datePickerRef.current?.open(); + }} + > + { + setCustomData( + attr, + dayjs(value).startOf('day').valueOf() + ); + }} + max={dayjs().toDate()} + > + {(value) => + value + ? dayjs(value).format('YYYY-MM-DD') + : '请选择日期' + } + + + )} +
+
+ ); +} diff --git a/src/components/user/manage/detail/index.json b/src/components/user/manage/detail/index.json new file mode 100644 index 000000000..1c24a5cb4 --- /dev/null +++ b/src/components/user/manage/detail/index.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "用户详情", + "usingComponents": { + "actionPanel": "../../../../components/func/actionPanel/index", + "oak-icon": "../../../../components/icon/index", + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-tag": "@oak-frontend-base/miniprogram_npm/lin-ui/tag/index" + } +} diff --git a/src/components/user/manage/detail/index.less b/src/components/user/manage/detail/index.less new file mode 100644 index 000000000..d4eae92b5 --- /dev/null +++ b/src/components/user/manage/detail/index.less @@ -0,0 +1,84 @@ +/** index.wxss **/ +@import "../../../../config/styles/mp/index.less"; +@import "../../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + + +.avatar { + font-size: 128rpx; + color: @oak-text-color-secondary; + height: 140rpx; + width: 140rpx; + border-radius: 14rpx; +} + +.nickname { + font-size: 44rpx; + font-weight: bold; +} + +.col { + display: flex; + flex-direction: column; + padding: 0rpx 10rpx; + border-bottom: 1px solid #e0e0e0; + background-color: #fff; + + .left { + display: flex; + flex-direction: row; + } + .icon { + width: 16rpx; + margin-left: 6rpx; + margin-right: 26rpx; + text-align: center; + } + .label { + color: @oak-text-color-primary; + } + .value { + color: @oak-text-color-secondary; + margin-right: 16rpx; + + .primary { + background-color: @oak-color-primary; + } + + .success { + background-color: @oak-color-success; + } + + .danger { + background-color: @oak-color-error; + } + + .warning { + background-color: @oak-color-warning; + } + } +} + + +.userInfo { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 300rpx; +} + +.btn-view { + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/src/components/user/manage/detail/index.ts b/src/components/user/manage/detail/index.ts new file mode 100644 index 000000000..9cd832baf --- /dev/null +++ b/src/components/user/manage/detail/index.ts @@ -0,0 +1,205 @@ +// index.ts + + +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + nickname: 1, + name: 1, + userState: 1, + birth: 1, + idState: 1, + gender: 1, + isRoot: 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, + }, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + }, + }, + ref: { + id: 1, + userState: 1, + }, + refId: 1, + }, + isList: false, + + formData: ({ data: user, features }) => { + const { + id, + nickname, + idState, + userState, + name, + gender, + mobile$user, + birth, + extraFile$entity, + } = user || {}; + const legalActions = user?.['#oakLegalActions'] || []; + const mobile = mobile$user && mobile$user[0]?.mobile; + const mobileCount = mobile$user ? mobile$user.length : 0; + const mobileText = + mobileCount && mobileCount > 1 + ? `${mobileCount}条手机号` + : mobile || '未设置'; + const avatar = features.extraFile.getUrl( + extraFile$entity && extraFile$entity[0] + ); + const reallyRoot = features.token.isReallyRoot(); + const currentUserId = features.token.getUserId(); + const executableActions = + reallyRoot && currentUserId !== id + ? legalActions.concat('play') + : legalActions; + return { + id, + nickname, + name, + mobile, + gender, + avatar, + birth: birth ? new Date(birth).toLocaleDateString() : '', + userState, + idState, + mobileCount, + mobileText, + executableActions, + }; + }, + actions: [ + 'accept', + 'activate', + 'disable', + 'enable', + 'remove', + 'update', + 'verify', + ], + data: { + stateColor: { + shadow: 'primary', + normal: 'success', + disabled: 'danger', + }, + idStateColor: { + verifying: 'primary', + verified: 'success', + unverified: 'warning', + }, + show: false, + actionDescriptions: { + accept: { + icon: { + name: 'success', + }, + label: '通过', + }, + activate: { + icon: { + name: 'playon', + }, + label: '激活', + }, + disable: { + icon: { + name: 'shielding', + }, + label: '禁用', + }, + enable: { + icon: { + name: 'barrage', + }, + label: '启用', + }, + remove: { + icon: { + name: 'trash', + }, + label: '删除', + }, + update: { + icon: { + name: 'editor', + }, + label: '更新', + }, + verify: { + icon: { + name: 'businesscard', + }, + label: '验证', + }, + play: { + icon: { + name: 'refresh', + }, + label: '切换', + }, + }, + genderOptions: { + male: '男', + female: '女', + }, + }, + methods: { + async onActionClick(action: string) { + switch (action) { + case 'update': { + this.navigateTo({ + url: '/user/manage/upsert', + oakId: this.props.oakId, + }); + return; + } + case 'enable': + case 'disable': + case 'accept': + case 'verify': + case 'activate': { + await this.execute(action); + break; + } + case 'play': { + const { id } = this.state; + await this.features.token.switchTo(id!); + break; + } + default: { + console.error(`尚未实现的action: ${action}`); + } + } + if (action === 'play') { + this.navigateBack(2); + } + }, + onActionClickMp(e: WechatMiniprogram.TouchEvent) { + const { action } = e.detail; + return this.onActionClick(action); + }, + }, +}); diff --git a/src/components/user/manage/detail/index.xml b/src/components/user/manage/detail/index.xml new file mode 100644 index 000000000..daa588306 --- /dev/null +++ b/src/components/user/manage/detail/index.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + {{nickname || '未设置'}} + + + {{name || '未设置'}} + + + {{genderOptions[gender] || '未设置'}} + + + {{birth || '未设置'}} + + + {{mobileText}} + + + + + {{userState || '未设置'}} + + + + + + + {{idState || '未设置'}} + + + + + + + \ No newline at end of file diff --git a/src/components/user/manage/detail/locales/zh_CN.json b/src/components/user/manage/detail/locales/zh_CN.json new file mode 100644 index 000000000..3b52cf392 --- /dev/null +++ b/src/components/user/manage/detail/locales/zh_CN.json @@ -0,0 +1,5 @@ +{ + "mobile": "手机号", + "unset": "未设置", + "avatar": "头像" +} diff --git a/src/components/user/manage/detail/mobile.module.less b/src/components/user/manage/detail/mobile.module.less new file mode 100644 index 000000000..9532034a6 --- /dev/null +++ b/src/components/user/manage/detail/mobile.module.less @@ -0,0 +1,13 @@ + + +.container { + height: 100%; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-page); +} + + +.list { + margin-bottom: 10px; +} \ No newline at end of file diff --git a/src/components/user/manage/detail/web.module.less b/src/components/user/manage/detail/web.module.less new file mode 100644 index 000000000..0dad10def --- /dev/null +++ b/src/components/user/manage/detail/web.module.less @@ -0,0 +1,8 @@ + + +.container { + background: var(--oak-bg-color-container); + box-shadow: 0 2px 3px #0000001a; + border-radius: 3px; + padding: 30px 32px; +} \ No newline at end of file diff --git a/src/components/user/manage/detail/web.pc.tsx b/src/components/user/manage/detail/web.pc.tsx new file mode 100644 index 000000000..31b06c335 --- /dev/null +++ b/src/components/user/manage/detail/web.pc.tsx @@ -0,0 +1,112 @@ +import React from 'react'; + +import { Tag, Avatar, Descriptions } from 'antd'; + +import Style from './web.module.less'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../../oak-app-domain'; +import ActionPanel from '../../../../components/func/actionPanel'; +import PageHeader from '../../../../components/common/pageHeader'; + + +export default function render( + props: WebComponentProps< + EntityDict, + 'user', + false, + { + nickname?: string; + avatar?: string; + name?: string; + mobile?: string; + userState?: string; + birth?: string; + idState?: string; + gender?: string; + stateColor: Record; + idStateColor: Record; + mobileCount: number; + mobileText: string; + actionDescriptions: Record< + string, + { + icon: { name: string }; + label: string; + } + >; + executableActions: string[]; + }, + { + onActionClick: (action: string) => Promise; + } + > +) { + const { + nickname, + avatar, + name, + userState, + idState, + gender, + stateColor, + idStateColor, + mobileText, + executableActions, + actionDescriptions, + birth, + } = props.data; + const { t, onActionClick } = props.methods; + + return ( + +
+ + onActionClick(action) + } + /> + } + > + + {avatar ? : t('unset')} + + + + {nickname || t('unset')} + + + + {name || t('unset')} + + + + {gender ? t(`user:v.gender.${gender}`) : t('unset')} + + + + {birth || t('unset')} + + + + {mobileText || t('unset')} + + + + + {t(`user:v.userState.${userState}`)} + + + + + {t(`user:v.idState.${idState}`)} + + + +
+
+ ); +} diff --git a/src/components/user/manage/detail/web.tsx b/src/components/user/manage/detail/web.tsx new file mode 100644 index 000000000..7965fe324 --- /dev/null +++ b/src/components/user/manage/detail/web.tsx @@ -0,0 +1,115 @@ +import React from 'react'; + +import { List, Tag, Avatar } from 'antd-mobile'; + +import Style from './mobile.module.less'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../../oak-app-domain'; +import ActionPanel from '../../../../components/func/actionPanel'; + + +export default function render( + props: WebComponentProps< + EntityDict, + 'user', + false, + { + nickname?: string; + avatar?: string; + name?: string; + mobile?: string; + userState?: string; + birth?: string; + idState?: string; + gender?: string; + stateColor: Record; + idStateColor: Record; + mobileCount: number; + mobileText: string; + actionDescriptions: Record< + string, + { + icon: { name: string }; + label: string; + } + >; + executableActions: string[]; + }, + { + onActionClick: (action: string) => Promise; + } + > +) { + const { + nickname, + avatar, + name, + userState, + idState, + gender, + stateColor, + idStateColor, + mobileText, + executableActions, + actionDescriptions, + birth, + } = props.data; + const { t, onActionClick } = props.methods; + + return ( +
+ + : t('unset')} + > + {t('avatar')} + + + + {t('user:attr.nickname')} + + + + {t('user:attr.name')} + + + + {t('user:attr.gender')} + + + + {t('user:attr.birth')} + + + {t('mobile')} + + + {t(`user:v.userState.${userState}`)} + + } + > + {t('user:attr.userState')} + + + + {t(`user:v.idState.${idState}`)} + + } + > + {t('user:attr.idState')} + + + onActionClick(action)} + /> +
+ ); +} diff --git a/src/components/user/manage/index.json b/src/components/user/manage/index.json new file mode 100644 index 000000000..dee95e0ca --- /dev/null +++ b/src/components/user/manage/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "用户管理", + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "l-tag": "@oak-frontend-base/miniprogram_npm/lin-ui/tag/index", + "l-avatar": "@oak-frontend-base/miniprogram_npm/lin-ui/avatar/index" + } +} diff --git a/src/components/user/manage/index.less b/src/components/user/manage/index.less new file mode 100644 index 000000000..e95c0b12f --- /dev/null +++ b/src/components/user/manage/index.less @@ -0,0 +1,77 @@ +/** index.wxss **/ +@import "../../../config/styles/mp/index.less"; +@import "../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.user-list { + flex: 1; + display: flex; + flex-direction: column; +} + +.user-info { + display: flex; + flex-direction: column; + flex: 1; + margin-left: 36rpx; +} + +.cell { + display: flex; + align-items: center; + padding: 26rpx; + border-bottom: 1px solid #e0e0e0; + background-color: #fff; +} + +.row { + display: flex; + align-items: center; + min-width: 300rpx; + justify-content: space-between; + margin-bottom: 20rpx; + + .primary { + background-color: @oak-color-primary; + } + + .success { + background-color: @oak-color-success; + } + + .danger { + background-color: @oak-color-error; + } +} + +.row2 { + display: flex; + align-items: center; + font-size: small; + .label { + min-width: 100rpx; + color: @oak-text-color-primary; + } + .value { + color: @oak-text-color-secondary; + } +} + +.add { + position: fixed; + right: 20rpx; + z-index: 10; + bottom: constant(safe-area-inset-bottom) !important; + /* 兼容 iOS < 11.2 */ + bottom: env(safe-area-inset-bottom) !important; + /* 兼容 iOS >= 11.2 */ +} \ No newline at end of file diff --git a/src/components/user/manage/index.ts b/src/components/user/manage/index.ts new file mode 100644 index 000000000..94fd643af --- /dev/null +++ b/src/components/user/manage/index.ts @@ -0,0 +1,126 @@ +// index.ts + + +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + nickname: 1, + name: 1, + userState: 1, + idState: 1, + extraFile$entity: { + $entity: 'extraFile', + data: { + id: 1, + tag1: 1, + origin: 1, + bucket: 1, + objectId: 1, + filename: 1, + extra1: 1, + type: 1, + entity: 1, + entityId: 1, + extension: 1, + }, + filter: { + tag1: 'avatar', + }, + indexFrom: 0, + count: 1, + }, + mobile$user: { + $entity: 'mobile', + data: { + id: 1, + mobile: 1, + }, + }, + }, + sorters: [ + { + sorter: () => ({ + $attr: { + $$createAt$$: 1, + }, + $direction: 'desc', + }), + }, + ], + isList: true, + formData: function ({ data: users, features }) { + const userArr = users.map((user) => { + const { + id, + nickname, + userState, + name, + mobile$user, + extraFile$entity, + } = user || {}; + const mobile = mobile$user && mobile$user[0]?.mobile; + const avatar = features.extraFile.getUrl( + extraFile$entity && extraFile$entity[0] + ); + return { + id, + nickname, + name, + mobile, + avatar, + userState, + }; + }); + const isRoot = features.token.isReallyRoot(); + + return { + userArr, + isRoot, + }; + }, + properties: { + event: '', + }, + data: { + stateColor: { + shadow: 'primary', + normal: 'success', + disabled: 'danger', + }, + }, + methods: { + async bindClicked(input: WechatMiniprogram.Input) { + // resolveInput拿的是target,原来代码拿的是currentTarget + const { dataset } = this.resolveInput(input); + const { id } = dataset!; + this.onCellClicked(id); + }, + async onCellClicked(id: string) { + const { event } = this.props; + if (event) { + this.pubEvent( + event, + this.state.userArr.find( + (ele) => ele.id === id + ) + ); + } else { + this.navigateTo({ + url: '/user/manage/detail', + oakId: id, + }); + } + }, + goNewUser() { + this.navigateTo({ + url: '/user/manage/upsert', + }); + }, + }, + lifetimes: { + detached() { + this.unsubAllEvents(this.props.event!); + }, + }, +}); \ No newline at end of file diff --git a/src/components/user/manage/index.xml b/src/components/user/manage/index.xml new file mode 100644 index 000000000..a01279b27 --- /dev/null +++ b/src/components/user/manage/index.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/user/manage/mobile.module.less b/src/components/user/manage/mobile.module.less new file mode 100644 index 000000000..943d1e418 --- /dev/null +++ b/src/components/user/manage/mobile.module.less @@ -0,0 +1,41 @@ + + +.container { + height: 100vh; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-page); +} + + +.avatar { + height: 56px; + width: 56px; + background-color: var(--oak-color-primary); + + .text { + color: #fff; + font-size: 16px; + } +} + +.description { + .row { + .label { + color: var(--oak-text-color-secondary); + font-size: 12px; + } + + .value { + color: var(--oak-text-color-primary); + font-size: 12px; + } + } +} + +.fab { + position: fixed; + bottom: 50px; + right: 10px; + z-index: 1; +} \ No newline at end of file diff --git a/src/components/user/manage/upsert/index.json b/src/components/user/manage/upsert/index.json new file mode 100644 index 000000000..f6190f1e2 --- /dev/null +++ b/src/components/user/manage/upsert/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "编辑用户信息", + "usingComponents": { + "l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index", + "l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index", + "l-input": "@oak-frontend-base/miniprogram_npm/lin-ui/input/index", + "l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index", + "mobile-manage-list": "../../../../components/mobile/manageList/index", + "oak-extraFile-avatar": "../../../../components/extraFile/avatar/index" + } +} diff --git a/src/components/user/manage/upsert/index.less b/src/components/user/manage/upsert/index.less new file mode 100644 index 000000000..442707f4d --- /dev/null +++ b/src/components/user/manage/upsert/index.less @@ -0,0 +1,59 @@ +/** index.wxss **/ +@import "../../../../config/styles/mp/index.less"; +@import "../../../../config/styles/mp/mixins.less"; + +.page-body { + height: 100vh; + display: flex; + flex: 1; + flex-direction: column; + background-color: @oak-bg-color-page; + box-sizing: border-box; + .safe-area-inset-bottom(); +} + +.list-box { + padding-left: 25rpx; + padding-right: 25rpx; +} + +.avatar { + margin: 10rpx 0; +} + +.col { + display: flex; + flex-direction: column; + background-color: #fff; + + .label { + color: @oak-text-color-primary; + } +} + +.list-item { + position: relative; + font-size: 28rpx; + color: #333; + min-height: 88rpx; + display: flex; + flex-direction: row; + align-items: center; + padding-right: 25rpx; + box-sizing: border-box; + + .label { + width: 100px; + display: flex; + flex-direction: row; + align-items: center; + height: 88rpx; + padding-left: 25rpx; + padding-right: 15rpx; + box-sizing: border-box; + } + + .mobile-box { + flex: 1; + } +} \ No newline at end of file diff --git a/src/components/user/manage/upsert/index.ts b/src/components/user/manage/upsert/index.ts new file mode 100644 index 000000000..9b6e105c4 --- /dev/null +++ b/src/components/user/manage/upsert/index.ts @@ -0,0 +1,161 @@ +import { EntityDict } from "../../../../oak-app-domain"; + +const GenderOptions = [ + { + value: 'male', label: '男', + }, + { + value: 'female', label: '女', + } +]; + +const IDCardTypeOptions = [ + { + value: 'ID-Card', label: '身份证', + }, + { + value: 'passport', label: '护照', + }, + { + value: 'Mainland-passport', label: '港澳通行证', + } +]; + +export default OakComponent({ + entity: 'user', + projection: { + id: 1, + name: 1, + nickname: 1, + birth: 1, + gender: 1, + idCardType: 1, + idNumber: 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, + }, + }, + isList: false, + formData({ data: user }) { + const isRoot = this.features.token.isRoot(); + const { birth, gender, idCardType } = user || {}; + const birthDate = birth && new Date(birth); + const birthText = birthDate && birthDate.toLocaleDateString(); + const birthDayValue = + birthDate && + `${birthDate.getFullYear()}-${birthDate.getMonth() + 1 + }-${birthDate.getDate()}`; + const genderOption = + gender && GenderOptions.find((ele) => ele.value === gender); + const genderText = genderOption && genderOption.label; + const genderOptionIndex = + genderOption && GenderOptions.indexOf(genderOption); + const idCardTypeOption = + idCardType && + IDCardTypeOptions.find((ele) => ele.value === idCardType); + const idCardTypeText = idCardTypeOption && idCardTypeOption.label; + const idCardTypeOptionIndex = + idCardTypeOption && IDCardTypeOptions.indexOf(idCardTypeOption); + const idCardTypeIndex = + idCardType && IDCardTypeOptions.find((ele) => ele.value === gender); + const now = new Date(); + return Object.assign({}, user, { + isRoot, + birthText, + birthDayValue, + genderText, + idCardTypeText, + idCardTypeOptionIndex, + oldestBirthday: `${now.getFullYear() - 120}-01-01`, + today: `${now.getFullYear()}-${now.getMonth() + 1 + }-${now.getDate()}`, + genderOptionIndex, + idCardTypeIndex, + }); + }, + data: { + birthEnd: '', // for小程序的picker + GenderOptions, + IDCardTypeOptions, + birthVisible: false, + }, + lifetimes: { + ready() { + const today = new Date(); + const birthEnd = `${today.getFullYear()}-${today.getMonth() + 1 + }-${today.getDate()}`; + this.setState({ birthEnd }); + }, + }, + methods: { + setValueMp(input: WechatMiniprogram.Input) { + const { + detail, + target: { dataset }, + } = input; + const { attr } = dataset!; + const { value } = detail; + this.update({ [attr]: value }); + }, + async confirm() { + const { nickname } = this.state; + if (!nickname) { + this.setMessage({ + type: 'warning', + content: '请输入昵称' + }) + return + } + await this.execute(); + this.navigateBack(); + }, + onBirthChange(e: WechatMiniprogram.Input) { + const { + detail: { value }, + } = e; + const birth = new Date(value); + this.update({ birth }); + }, + onIdCardTypeChange(e: any) { + const { + detail: { value: index }, + } = e; + const { value } = IDCardTypeOptions[index]; + this.update({ + idCardType: + value as EntityDict['user']['OpSchema']['idCardType'], + }); + }, + onGenderChange(e: any) { + const { + detail: { value: index }, + } = e; + const { value } = GenderOptions[index]; + this.update({ + gender: value as EntityDict['user']['OpSchema']['gender'], + }); + }, + setMobile() { + this.navigateTo({ + url: '/mobile/me', + }); + }, + }, +}); \ No newline at end of file diff --git a/src/components/user/manage/upsert/index.xml b/src/components/user/manage/upsert/index.xml new file mode 100644 index 000000000..7f899fc30 --- /dev/null +++ b/src/components/user/manage/upsert/index.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 手机号 + + + + + + + + + {{mobile || '未绑定'}} + + + + + + + + {{t('common::action.confirm')}} + + \ No newline at end of file diff --git a/src/components/user/manage/upsert/mobile.module.less b/src/components/user/manage/upsert/mobile.module.less new file mode 100644 index 000000000..bec04c19c --- /dev/null +++ b/src/components/user/manage/upsert/mobile.module.less @@ -0,0 +1,12 @@ + + +.container { + height: 100vh; + display: flex; + flex-direction: column; + background-color: var(--oak-bg-color-container); +} + +.radio { + margin-right: 10px; +} diff --git a/src/components/user/manage/upsert/web.module.less b/src/components/user/manage/upsert/web.module.less new file mode 100644 index 000000000..0dad10def --- /dev/null +++ b/src/components/user/manage/upsert/web.module.less @@ -0,0 +1,8 @@ + + +.container { + background: var(--oak-bg-color-container); + box-shadow: 0 2px 3px #0000001a; + border-radius: 3px; + padding: 30px 32px; +} \ No newline at end of file diff --git a/src/components/user/manage/upsert/web.pc.tsx b/src/components/user/manage/upsert/web.pc.tsx new file mode 100644 index 000000000..4a0773f58 --- /dev/null +++ b/src/components/user/manage/upsert/web.pc.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import { Button, Checkbox, Input, Form, Radio, DatePicker, Space } from 'antd'; +import dayjs from 'dayjs'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../../oak-app-domain'; +import PageHeader from '../../../../components/common/pageHeader'; +import Style from './web.module.less'; + + +export default function Render( + props: WebComponentProps< + EntityDict, + 'user', + false, + { + nickname?: string; + name?: string; + gender?: string; + birth?: string; + idCardType?: string; + idNumber?: string; + GenderOptions: Array<{ value: 'male' | 'female'; label: string }>; + IDCardTypeOptions: Array<{ value: string; label: string }>; + }, + { + confirm: () => void; + } + > +) { + const { data, methods } = props; + const { GenderOptions, IDCardTypeOptions } = data; + + const { t, update, confirm } = methods; + + return ( + +
+
+ + + update({ nickname: e.target.value }) + } + value={data.nickname || ''} + /> + + + update({ name: e.target.value })} + value={data.name || ''} + /> + + + + update({ birth: dayjs(value).valueOf() }) + } + /> + + + + { + update({ + gender: e.target + .value as EntityDict['user']['OpSchema']['gender'], + }); + }} + value={data.gender} + > + {GenderOptions.map((ele, idx) => ( + + {ele.label} + + ))} + + + + { + update({ + idCardType: e.target + .value as EntityDict['user']['OpSchema']['idCardType'], + }); + }} + value={data.idCardType} + > + {IDCardTypeOptions.map((ele, idx) => ( + + {ele.label} + + ))} + + + + + update({ idNumber: e.target.value }) + } + value={data.idNumber || ''} + /> + + + + + +
+
+
+ ); +} diff --git a/src/components/user/manage/upsert/web.tsx b/src/components/user/manage/upsert/web.tsx new file mode 100644 index 000000000..30f852c96 --- /dev/null +++ b/src/components/user/manage/upsert/web.tsx @@ -0,0 +1,141 @@ +import React, { useState } from 'react'; +import { Button, Checkbox, Input, Form, Radio, DatePicker, Space } from 'antd-mobile'; +import dayjs from 'dayjs'; +import Style from './mobile.module.less'; +import { EntityDict } from '../../../../oak-app-domain'; +import { WebComponentProps } from 'oak-frontend-base'; + + +export default function Render( + props: WebComponentProps< + EntityDict, + 'user', + false, + { + nickname?: string; + name?: string; + gender?: string; + birth?: string; + idCardType?: string; + idNumber?: string; + GenderOptions: Array<{ value: 'male' | 'female'; label: string }>; + IDCardTypeOptions: Array<{ value: string; label: string }>; + }, + { + confirm: () => void; + } + > +) { + const { data, methods } = props; + const { GenderOptions, IDCardTypeOptions } = data; + + const { t, update, setDisablePulldownRefresh, confirm } = methods; + const [birthPickerVisible, setBirthPickerVisible] = useState(false); + + return ( +
+
+ + update({ nickname: val })} + value={data.nickname || ''} + /> + + + update({ name: val })} + value={data.name || ''} + /> + + { + setBirthPickerVisible(true); + setDisablePulldownRefresh(true); + }} + > + + + + + { + update({ + gender: e as EntityDict['user']['OpSchema']['gender'], + }); + }} + value={data.gender} + > + + {GenderOptions.map((ele, idx) => ( + + {ele.label} + + ))} + + + + + { + update({ + idCardType: + e as EntityDict['user']['OpSchema']['idCardType'], + }); + }} + value={data.idCardType} + > + + {IDCardTypeOptions.map((ele, idx) => ( + + {ele.label} + + ))} + + + + + update({ idNumber: val })} + value={data.idNumber || ''} + /> + +
+ { + const val = value.valueOf(); + update({ birth: val }); + }} + onClose={() => { + setBirthPickerVisible(false); + setDisablePulldownRefresh(false); + }} + /> +
+ +
+ ); +} diff --git a/src/components/user/manage/web.module.less b/src/components/user/manage/web.module.less new file mode 100644 index 000000000..0dad10def --- /dev/null +++ b/src/components/user/manage/web.module.less @@ -0,0 +1,8 @@ + + +.container { + background: var(--oak-bg-color-container); + box-shadow: 0 2px 3px #0000001a; + border-radius: 3px; + padding: 30px 32px; +} \ No newline at end of file diff --git a/src/components/user/manage/web.pc.tsx b/src/components/user/manage/web.pc.tsx new file mode 100644 index 000000000..2a90cca2d --- /dev/null +++ b/src/components/user/manage/web.pc.tsx @@ -0,0 +1,165 @@ +import React from 'react'; +import { Table, Tag, Button, Modal, Space, Avatar } from 'antd'; +import { UserOutlined } from '@ant-design/icons'; +import { WebComponentProps } from 'oak-frontend-base'; +import FilterPanel from 'oak-frontend-base/es/components/filterPanel'; +import { EntityDict } from '../../../oak-app-domain'; +import PageHeader from '../../../components/common/pageHeader'; +import Style from './web.module.less'; + +export default function Render( + props: WebComponentProps< + EntityDict, + 'user', + true, + { + userArr: Array< + EntityDict['user']['OpSchema'] & { + avatar: string; + mobile: string; + } + >; + stateColor: Record; + isRoot: boolean; + }, + { + onCellClicked: (id: string, event?: string) => Promise; + goNewUser: () => Promise; + } + > +) { + const { methods, data } = props; + const { t, setPageSize, setCurrentPage, goNewUser, onCellClicked } = + methods; + const { + oakFullpath, + oakLoading, + oakPagination, + userArr = [], + stateColor, + isRoot, + } = data; + const { pageSize, total, currentPage } = oakPagination || {}; + + return ( + +
+ {isRoot && ( + + + + )} + + + + { + return index + 1; + }, + }, + { + width: 100, + dataIndex: 'avatar', + title: '头像', + render: (value, record, index) => { + if (!value) { + return ( + } + > + ); + } + return ; + }, + }, + { + dataIndex: 'nickname', + title: '昵称', + }, + { + dataIndex: 'name', + title: '姓名', + }, + { + dataIndex: 'mobile', + title: '手机号', + }, + { + dataIndex: 'userState', + title: '状态', + render: (value, record, index) => { + return ( + + {t(`user:v.userState.${value}`)} + + ); + }, + }, + { + dataIndex: 'op', + width: 200, + title: '操作', + align: 'center', + render: (value, record, index) => { + return ( + <> + + + ); + }, + fixed: 'right', + }, + ]} + pagination={{ + total: total, + pageSize: pageSize, + current: currentPage, + onShowSizeChange: (pageSize: number) => { + setPageSize(pageSize); + }, + onChange: (page: number) => { + setCurrentPage(page); + }, + }} + /> + + + ); +} diff --git a/src/components/user/manage/web.tsx b/src/components/user/manage/web.tsx new file mode 100644 index 000000000..c01bb5f30 --- /dev/null +++ b/src/components/user/manage/web.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { Button, List, Tag, Avatar, FloatingBubble } from 'antd-mobile'; +import { PlusOutlined } from '@ant-design/icons'; +import { WebComponentProps } from 'oak-frontend-base'; +import { EntityDict } from '../../../oak-app-domain'; +import Style from './mobile.module.less'; + +export default function render( + props: WebComponentProps< + EntityDict, + 'user', + true, + { + userArr: Array< + EntityDict['user']['OpSchema'] & { + avatar: string; + mobile: string; + } + >; + stateColor: Record; + isRoot: boolean; + }, + { + onCellClicked: (id: string) => Promise; + goNewUser: () => Promise; + } + > +) { + const { stateColor, userArr, isRoot } = props.data; + const { onCellClicked, t, goNewUser } = props.methods; + return ( +
+ + {userArr?.map((ele, index) => { + return ( + onCellClicked(ele.id)} + prefix={ + + } + title={
{ele.name || '--'}
} + description={ +
+
+ + 昵称:  + + + {ele.nickname || '--'} + +
+
+ + 手机号:  + + + {ele.mobile || '--'} + +
+ + {ele.userState + ? t( + `user:v.userState.${ele.userState}` + ) + : '未知'} + +
+ } + >
+ ); + })} +
+ + {isRoot && ( + { + goNewUser(); + }} + > + + + )} +
+ ); +} diff --git a/src/components/userEntityGrant/upsert/index.ts b/src/components/userEntityGrant/upsert/index.ts index 34cf7be9b..9ab7b650d 100644 --- a/src/components/userEntityGrant/upsert/index.ts +++ b/src/components/userEntityGrant/upsert/index.ts @@ -24,7 +24,7 @@ export default OakComponent({ entityId: '', relationEntity: '', relationEntityFilter: {} as any, - relationIds: [] as EntityDict['relation']['OpSchema'][], + relationIds: [] as string[], type: 'grant' as EntityDict['userEntityGrant']['Schema']['type'], redirectToAfterConfirm: {} as EntityDict['userEntityGrant']['Schema']['redirectTo'], @@ -32,7 +32,8 @@ export default OakComponent({ qrCodeType: '' as QrCodeType, multiple: false, rule: 'single' as EntityDict['userEntityGrant']['OpSchema']['rule'], - ruleOnRow: 'single' as EntityDict['userEntityGrant']['OpSchema']['ruleOnRow'], + ruleOnRow: + 'single' as EntityDict['userEntityGrant']['OpSchema']['ruleOnRow'], }, lifetimes: { ready() { @@ -110,7 +111,6 @@ export default OakComponent({ this.setState({ userEntityGrantId: id, }); - }, - } + }, }); \ No newline at end of file diff --git a/src/components/userRelation/upsert/byMobile/index.xml b/src/components/userRelation/upsert/byMobile/index.xml index 40ba9d0ca..217371698 100644 --- a/src/components/userRelation/upsert/byMobile/index.xml +++ b/src/components/userRelation/upsert/byMobile/index.xml @@ -23,7 +23,7 @@ style="display: flex;flex-direction: column; flex: 1; margin-top: 30rpx" oakId="{{userId}}" mobile="{{mobileValue}}" - oakPath="{{oakFullpath ? oakFullpath + '.user' : undefined}}" + oakPath="{{oakFullpath + '.user'}}" oakAutoUnmount="{{true}}" entity="{{entity}}" entityId="{{entityId}}" diff --git a/src/components/userRelation/upsert/byMobile/web.pc.tsx b/src/components/userRelation/upsert/byMobile/web.pc.tsx index 4c630b016..2c900e0f0 100644 --- a/src/components/userRelation/upsert/byMobile/web.pc.tsx +++ b/src/components/userRelation/upsert/byMobile/web.pc.tsx @@ -59,7 +59,7 @@ export default function Render(props: WebComponentProps - + diff --git a/src/components/userRelation/upsert/byUser/web.pc.tsx b/src/components/userRelation/upsert/byUser/web.pc.tsx index f4ac09e19..29daf303a 100644 --- a/src/components/userRelation/upsert/byUser/web.pc.tsx +++ b/src/components/userRelation/upsert/byUser/web.pc.tsx @@ -20,7 +20,7 @@ export default function Render(props: WebComponentProps ele === key); if (index > -1) { relationIds.splice(index, 1); @@ -150,7 +150,7 @@ export default OakComponent({ checked: relationIds.includes(ele.id), }) ); - this.setState({ relations: newRelations }); + this.setState({ relations: newRelations } as any); this.setRelation(relationIds); }, setNumber(value: number) { @@ -170,14 +170,14 @@ export default OakComponent({ this.setPeriod(count); }, setMultiple(m: boolean) { - this.setState({ multiple: m }); + this.update({ multiple: m }); }, setMultipleMp(e: any) { const { value } = e.detail; this.setMultiple(value); }, - setRule(m: string) { - this.setState({ rule: m }); + setRule(m: EntityDict['userEntityGrant']['Schema']['rule']) { + this.update({ rule: m }); }, setRuleMp(e: any) { const { currentKey } = e.detail; diff --git a/src/components/userRelation/upsert/byUserEntityGrant/index.xml b/src/components/userRelation/upsert/byUserEntityGrant/index.xml index 5b4ba62ed..7881f3f5d 100644 --- a/src/components/userRelation/upsert/byUserEntityGrant/index.xml +++ b/src/components/userRelation/upsert/byUserEntityGrant/index.xml @@ -10,7 +10,7 @@ 分享 - @@ -47,7 +47,7 @@ {{t('userEntityGrant:attr.expiresAt')}}: - + diff --git a/src/components/userRelation/upsert/onUser/index.xml b/src/components/userRelation/upsert/onUser/index.xml index 34dd0614a..20edd5ba4 100644 --- a/src/components/userRelation/upsert/onUser/index.xml +++ b/src/components/userRelation/upsert/onUser/index.xml @@ -2,7 +2,7 @@ 权限: - + diff --git a/src/components/userRelation/upsert/onUser/web.pc.tsx b/src/components/userRelation/upsert/onUser/web.pc.tsx index c04b665f3..ad482cc35 100644 --- a/src/components/userRelation/upsert/onUser/web.pc.tsx +++ b/src/components/userRelation/upsert/onUser/web.pc.tsx @@ -215,11 +215,7 @@ export default function Render(props: WebComponentProps void; - } - > -) { - const { mobiles, allowRemove, tokenMobileId, showBack = false } = props.data; - const { goAddMobile, removeItem, recoverItem, execute, subEvent } = props.methods; - const [open, setOpen] = useState(false); - const eventLoggedIn = `user:info:login:${Date.now()}`; - - return ( - -
- - -
- - {mobiles?.map((ele, index) => ( - { - const modal = Modal! - .confirm!({ - title: `确认删除吗?删除后无法用此号码登录`, - okText: '确定', - cancelText: '取消', - onOk: async (e) => { - removeItem(ele.id); - try { - await execute(); - } catch (err) { - recoverItem( - ele.id - ); - throw err; - } - modal!.destroy!(); - }, - onCancel: (e) => { - modal!.destroy!(); - }, - }); - }} - > - - - ) - } - > - } - title={ele.mobile} - > - - ))} - - - - - { - setOpen(false); - }} - > -
- { - setOpen(false); - }} - oakPath="$mobile/me-mobile/login" - oakAutoUnmount={true} - /> -
-
- - ); -} diff --git a/src/pages/user/info/web.pc.tsx b/src/pages/user/info/web.pc.tsx index 9f5309912..e1aaa8036 100644 --- a/src/pages/user/info/web.pc.tsx +++ b/src/pages/user/info/web.pc.tsx @@ -17,7 +17,7 @@ import { EntityDict } from '../../../oak-app-domain'; import PageHeader from '../../../components/common/pageHeader'; import OakAvatar from '../../../components/extraFile/avatar'; import ExtraFileCommit from '../../../components/extraFile/commit'; -import MobileLogin from '../../../pages/mobile/login'; +import MobileLogin from '../../../components/mobile/login'; import WechatLoginQrCode from '../../../components/wechatLogin/qrCode'; import WechatUserList from '../../../components/wechatUser/bindingList'; import { diff --git a/src/pages/user/manage/detail/index.ts b/src/pages/user/manage/detail/index.ts index 200728083..9cd832baf 100644 --- a/src/pages/user/manage/detail/index.ts +++ b/src/pages/user/manage/detail/index.ts @@ -47,7 +47,7 @@ export default OakComponent({ }, isList: false, - formData: ({ data: user, features, legalActions }) => { + formData: ({ data: user, features }) => { const { id, nickname, @@ -59,6 +59,7 @@ export default OakComponent({ birth, extraFile$entity, } = user || {}; + const legalActions = user?.['#oakLegalActions'] || []; const mobile = mobile$user && mobile$user[0]?.mobile; const mobileCount = mobile$user ? mobile$user.length : 0; const mobileText = diff --git a/src/pages/userEntityGrant/detail/mobile.module.less b/src/pages/userEntityGrant/detail/mobile.module.less deleted file mode 100644 index 07e0c4c2c..000000000 --- a/src/pages/userEntityGrant/detail/mobile.module.less +++ /dev/null @@ -1,7 +0,0 @@ - - -.container { - height: 100vh; - display: flex; - flex-direction: column; -} diff --git a/src/pages/userEntityGrant/detail/web.pc.tsx b/src/pages/userEntityGrant/detail/web.pc.tsx index 00903e4e9..a120e3fca 100644 --- a/src/pages/userEntityGrant/detail/web.pc.tsx +++ b/src/pages/userEntityGrant/detail/web.pc.tsx @@ -1,6 +1,4 @@ import React, { Component } from 'react'; -import Style from './web.module.less'; -import PageHeader from '../../../components/common/pageHeader'; import QrCode from '../../../components/common/qrCode'; import { WebComponentProps } from 'oak-frontend-base'; import { EntityDict } from '../../../oak-app-domain'; @@ -12,25 +10,19 @@ export default function Render( 'userEntityGrant', true, { - showBack: boolean; - variant: 'alone' | 'inline' | 'dialog'; url: string; expiresAt: number; - title?: string }, {} > ) { const { - variant, - showBack = true, url, expiresAt, - title = '授权二维码', oakLoading, } = props.data; return ( - + <> { oakLoading? (
) } - - ); -} - - -function Container(props: { - children: React.ReactNode; - variant?: 'inline' | 'alone' | 'dialog'; - showBack?: boolean; - title?: string; -}) { - const { - children, - variant = 'alone', - showBack, - title, - } = props; - if (['inline', 'dialog'].includes(variant)) { - return <>{children}; - } - return ( - -
{children}
-
+ ); } \ No newline at end of file diff --git a/src/pages/userEntityGrant/detail/web.tsx b/src/pages/userEntityGrant/detail/web.tsx index 00903e4e9..ad2ff1420 100644 --- a/src/pages/userEntityGrant/detail/web.tsx +++ b/src/pages/userEntityGrant/detail/web.tsx @@ -1,6 +1,4 @@ import React, { Component } from 'react'; -import Style from './web.module.less'; -import PageHeader from '../../../components/common/pageHeader'; import QrCode from '../../../components/common/qrCode'; import { WebComponentProps } from 'oak-frontend-base'; import { EntityDict } from '../../../oak-app-domain'; @@ -12,63 +10,32 @@ export default function Render( 'userEntityGrant', true, { - showBack: boolean; - variant: 'alone' | 'inline' | 'dialog'; url: string; expiresAt: number; - title?: string }, {} > ) { const { - variant, - showBack = true, url, expiresAt, - title = '授权二维码', oakLoading, } = props.data; return ( - - { - oakLoading? ( -
- -
- ) : ( - - ) - } -
- ); -} - - -function Container(props: { - children: React.ReactNode; - variant?: 'inline' | 'alone' | 'dialog'; - showBack?: boolean; - title?: string; -}) { - const { - children, - variant = 'alone', - showBack, - title, - } = props; - if (['inline', 'dialog'].includes(variant)) { - return <>{children}; - } - return ( - -
{children}
-
+ <> + {oakLoading ? ( +
+ +
+ ) : ( + + )} + ); } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 722dd0361..efcf82476 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -81,8 +81,6 @@ "lib", "es", "node_modules", - "src/pages/**/*", - "src/components/**/*", "**/*.spec.ts", "package.json", "tsconfig.json", diff --git a/tsconfig.lib.json b/tsconfig.lib.json new file mode 100644 index 000000000..722dd0361 --- /dev/null +++ b/tsconfig.lib.json @@ -0,0 +1,94 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "lib": [ + "esnext", + "DOM" + ], + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + "importHelpers": true, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "lib", /* Redirect output structure to the directory. */ + "rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "skipLibCheck": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, /* Disallow inconsistently-cased references to the same file. */ + "types": [ + "node", + "wechat-miniprogram", + "react" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.json", + "./typings/*.d.ts" + ], + "exclude": [ + "lib", + "es", + "node_modules", + "src/pages/**/*", + "src/components/**/*", + "**/*.spec.ts", + "package.json", + "tsconfig.json", + "tsconfig.es.json", + "package-lock.json", + "test", + "scripts" + ] +} \ No newline at end of file