提交 fcec7002 编写于 作者: ibizdev's avatar ibizdev

chitanda 发布系统代码 [TrainSys,网页端]

上级 17f9e35b
// dynamic front template
\ No newline at end of file
FROM registry.cn-shanghai.aliyuncs.com/ibizops/nginx-dynamic:v1
WORKDIR /
COPY dist /dist
\ No newline at end of file
# full-dynamic-vue
全动态版本vue,vite + vue2.7 + iview4.7 + tsx
\ No newline at end of file
/**
* 提交信息规范
* 提交格式: type: description
*
* feat: 新功能(feature)
* fix: 修补bug
* docs: 文档(documentation)
* style: 格式(不影响代码运行的变动)
* refactor: 重构(即不是新增功能,也不是修改bug的代码变动)
* test: 增加测试,或测试变更
* perf : 性能优化
* revert: 撤销上一次的提交
* build: 构建工具或构建过程等的变动,如:关联包升级等
* chore: 其他修改(不在上述类型中的修改)
* release: 发布新版本
*/
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'test',
'perf',
'revert',
'build',
'chore',
'release',
],
],
},
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link type="text/css" href="./css/app-loading.css" rel="stylesheet">
<script type="javascript" src="./environments/environment.js"></script>
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<div id="app-loading-x" class="app-loading-x">
<div class="app-loading-x-container">
<label></label>
<label></label>
<label></label>
<label></label>
<label></label>
<label></label>
</div>
</div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
pnpm link --global "@ibiz-template/vue-util"
pnpm link --global "@ibiz-template/service"
pnpm link --global "@ibiz-template/runtime"
pnpm link --global "@ibiz-template/model"
pnpm link --global "@ibiz-template/core"
pnpm link --global "@ibiz-template/command"
pnpm link --global "@ibiz-template/controller"
\ No newline at end of file
server {
listen 80;
server_name localhost;
location /web/ {
alias /dist/;
index index.html index.htm;
}
location /trainsys__web {
proxy_pass http://gateway.ibizcloud.cn:20086;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
\ No newline at end of file
{
"name": "vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"lint": "eslint 'src/**/*.ts' 'src/**/*.vue'",
"lint:style": "stylelint 'src/**/*.scss'",
"prepare": "husky install"
},
"dependencies": {
"@ibiz-template/command": "^0.0.1-alpha.2",
"@ibiz-template/controller": "^0.0.1-alpha.5",
"@ibiz-template/core": "^0.0.1-alpha.5",
"@ibiz-template/model": "^0.0.1-alpha.5",
"@ibiz-template/runtime": "^0.0.1-alpha.5",
"@ibiz-template/service": "^0.0.1-alpha.5",
"@ibiz-template/vue-util": "^0.0.1-alpha.5",
"lodash-es": "^4.17.21",
"pinia": "^2.0.22",
"proxy-polyfill": "^0.3.2",
"qs": "^6.11.0",
"qx-util": "^0.4.1",
"ramda": "^0.28.0",
"systemjs": "^6.12.6",
"view-design": "^4.7.0",
"vue": "^2.7.10",
"vue-router": "^3.6.4"
},
"devDependencies": {
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"@types/lodash-es": "^4.17.6",
"@types/node": "^18.7.18",
"@types/qs": "^6.9.7",
"@types/ramda": "^0.28.15",
"@types/systemjs": "^6.1.1",
"@typescript-eslint/eslint-plugin": "^5.38.0",
"@typescript-eslint/parser": "^5.38.0",
"@vitejs/plugin-legacy": "^2.2.0",
"@vitejs/plugin-vue2": "^2.0.0",
"@vitejs/plugin-vue2-jsx": "^1.0.3",
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
"eslint": "^8.23.1",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.5.1",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"rollup-plugin-visualizer": "^5.8.1",
"sass": "^1.55.0",
"stylelint": "^14.12.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recess-order": "^3.0.0",
"stylelint-config-standard": "^28.0.0",
"stylelint-config-standard-scss": "^5.0.0",
"stylelint-scss": "^4.3.0",
"terser": "^5.15.0",
"typescript": "^4.8.3",
"vite": "^3.1.3",
"vite-plugin-eslint": "^1.8.1",
"vue-eslint-parser": "^9.1.0",
"vue-tsc": "^0.40.13"
},
"lint-staged": {
"*.{ts,vue}": "eslint --fix",
"*.less": "stylelint --syntax=scss"
}
}
因为 它太大了无法显示 源差异 。您可以改为 查看blob
@charset "utf-8";
.app-loading-x {
position: absolute;
top: 0;
left: 0;
z-index: 10000;
width: 100%;
height: 100%;
overflow: hidden;
background: radial-gradient(#55A0FE, #1767CB);
}
.app-loading-x-container {
position: absolute;
top: 50%;
width: 100%;
color: #fff;
text-align: center;
transform: translateY(-50%);
}
.app-loading-x-container label {
display: inline-block;
font-size: 20px;
opacity: 0;
}
.app-loading-x-container label:nth-child(6) {
animation: loading 3s infinite ease-in-out
}
.app-loading-x-container label:nth-child(5) {
animation: loading 3s .1s infinite ease-in-out
}
.app-loading-x-container label:nth-child(4) {
animation: loading 3s .2s infinite ease-in-out
}
.app-loading-x-container label:nth-child(3) {
animation: loading 3s .3s infinite ease-in-out
}
.app-loading-x-container label:nth-child(2) {
animation: loading 3s .4s infinite ease-in-out
}
.app-loading-x-container label:nth-child(1) {
animation: loading 3s .5s infinite ease-in-out
}
@keyframes loading{
0% {
opacity:0;
transform:translateX(-300px)
}
33% {
opacity:1;
transform:translateX(0)
}
66% {
opacity:1;
transform:translateX(0)
}
100% {opacity:0;
transform:translateX(300px)
}
}
\ No newline at end of file
window.Environment = {
// 远端动态基础路径
remoteDynaPath: '/remotemodel',
// 应用请求基础路径
BaseUrl: 'pms__sclpmswebapp',
// 中心系统标识
mockDcSystemId: 'pms',
};
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
import { defineComponent } from 'vue';
export default defineComponent({
render() {
return (
<div class='app'>
<router-view />
</div>
);
},
});
import { VueConstructor } from 'vue';
import { install as installCore } from '@ibiz-template/core';
import { install as installService } from '@ibiz-template/service';
import { install as installRuntime } from '@ibiz-template/runtime';
import { install as installController } from '@ibiz-template/controller';
import ViewDesign from 'view-design';
import 'view-design/dist/styles/iview.css';
import {
AppLayout,
ControlLayout,
ViewBase,
ViewLayout,
} from './components/layout';
import {
AppMenu,
EditFormControl,
FormControl,
FormPage,
FormPageItem,
FormGroupPanel,
FormItemContainer,
FormItem,
GridControl,
SearchFormControl,
FormTabPanel,
FormTabPage,
FormRawItem,
FormButton,
FormDRUIPart,
GridColumn,
GridUAColumn,
GridFieldColumn,
GridEditItem,
ViewPanel,
PickupViewPanel,
} from './components/widgets';
import {
EditView,
GridView,
PickupGridView,
PickupView,
MPickupView,
OptView,
} from './components/views';
import { IndexView } from './views';
import AppKeepAlive from './components/common/app-keep-alive/app-keep-alive.vue';
import {
AppIcon,
ViewToolbar,
QuickSearch,
AppCol,
AppRow,
AppGridPagination,
AppGridEditItem,
ActionToolbar,
AppUser,
AppTransition,
} from './components/common';
// 编辑器组件
import IBizSpan from './components/editor/ibiz-span/ibiz-span';
import IBizInputBox from './components/editor/ibiz-input-box/ibiz-input-box';
import AppInputNumber from './components/editor/app-input-number/app-input-number';
import IBizCheckBoxList from './components/editor/ibiz-check-box-list/ibiz-check-box-list';
import IBizRadioButtonList from './components/editor/ibiz-radio-button-list/ibiz-radio-button-list';
import IBizDatePicker from './components/editor/ibiz-date-picker/ibiz-date-picker';
import IBizDropDownList from './components/editor/ibiz-dropdown-list/ibiz-dropdown-list';
import IBizPicker from './components/editor/ibiz-picker/ibiz-picker';
import AppSelectTree from './components/editor/app-select-tree/app-select-tree';
import IBizPickerDropDown from './components/editor/ibiz-picker-dropdown/ibiz-picker-dropdown';
import AppPickerLinkOnly from './components/editor/app-picker-linkonly/app-picker-linkonly';
import IBizMPicker from './components/editor/ibiz-mpicker/ibiz-mpicker';
import IBizFileUpload from './components/editor/ibiz-file-upload/ibiz-file-upload';
import AppFileUploadRowPreview from './components/editor/app-file-upload-row-preview/app-file-upload-row-preview';
import AppImageUpload from './components/editor/app-image-upload/app-image-upload';
import NotSupportedEditor from './components/editor/not-supported-editor/not-supported-editor';
import { presetEditorProvider } from './provider';
export const AppRegister = {
install(v: VueConstructor) {
installCore();
installService();
installRuntime();
installController();
presetEditorProvider();
v.use(ViewDesign);
// 注册布局组件
v.component('AppLayout', AppLayout);
v.component('ControlLayout', ControlLayout);
v.component('ViewLayout', ViewLayout);
v.component('ViewBase', ViewBase);
// 注册视图组件
v.component('IndexView', IndexView);
v.component('GridView', GridView);
v.component('EditView', EditView);
v.component('OptView', OptView);
v.component('PickupGridView', PickupGridView);
v.component('PickupView', PickupView);
v.component('MPickupView', MPickupView);
// 注册部件组件
v.component('AppMenu', AppMenu);
v.component('GridControl', GridControl);
v.component('GridColumn', GridColumn);
v.component('GridUaColumn', GridUAColumn);
v.component('GridFieldColumn', GridFieldColumn);
v.component('GridEditItem', GridEditItem);
v.component('FormButton', FormButton);
v.component('FormDruipart', FormDRUIPart);
v.component('FormGroupPanel', FormGroupPanel);
v.component('AppFormItem', FormItem);
v.component('FormPage', FormPage);
v.component('FormPageItem', FormPageItem);
v.component('FormRawItem', FormRawItem);
v.component('FormTabPage', FormTabPage);
v.component('FormTabPanel', FormTabPanel);
v.component('FormControl', FormControl);
v.component('EditFormControl', EditFormControl);
v.component('SearchFormControl', SearchFormControl);
v.component('ViewPanel', ViewPanel);
v.component('PickupViewPanel', PickupViewPanel);
// 注册通用组件
v.component('AppKeepAlive', AppKeepAlive);
v.component('AppIcon', AppIcon);
v.component('AppCol', AppCol);
v.component('AppRow', AppRow);
v.component('ViewToolbar', ViewToolbar);
v.component('ActionToolbar', ActionToolbar);
v.component('AppGridPagination', AppGridPagination);
v.component('FormItemContainer', FormItemContainer);
v.component('AppGridEditItem', AppGridEditItem);
v.component('AppUser', AppUser);
v.component('AppTransition', AppTransition);
// 注册编辑器组件
v.component('IBizSpan', IBizSpan);
v.component('IBizInputBox', IBizInputBox);
v.component('AppInputNumber', AppInputNumber);
v.component('IBizCheckBoxList', IBizCheckBoxList);
v.component('IBizRadioButtonList', IBizRadioButtonList);
v.component('IBizDatePicker', IBizDatePicker);
v.component('IBizDropDownList', IBizDropDownList);
v.component('IBizPicker', IBizPicker);
v.component('IBizPickerDropDown', IBizPickerDropDown);
v.component('AppPickerLinkOnly', AppPickerLinkOnly);
v.component('IBizMPicker', IBizMPicker);
v.component('IBizFileUpload', IBizFileUpload);
v.component('AppFileUploadRowPreview', AppFileUploadRowPreview);
v.component('AppImageUpload', AppImageUpload);
v.component('AppSelectTree', AppSelectTree);
v.component('QuickSearch', QuickSearch);
v.component('NotSupportedEditor', NotSupportedEditor);
},
};
export async function attachEnvironmentConfig(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const env = (window as any).Environment;
ibiz.env.baseUrl = env.BaseUrl;
ibiz.env.remoteModelUrl = env.remoteDynaPath;
ibiz.env.dcSystem = env.mockDcSystemId;
}
import { defineComponent, PropType, reactive } from 'vue';
import { IPSUIActionGroup, IPSUIActionGroupDetail } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/common/view-toolbar/view-toolbar.scss';
import { ActionStates, ActionState } from '@ibiz-template/controller';
export const ActionToolbar = defineComponent({
name: 'ActionToolbar',
props: {
actionGroup: {
type: Object as PropType<IPSUIActionGroup>,
required: true,
},
actionStates: {
type: Object as PropType<ActionStates>,
required: true,
},
},
setup(props, { emit }) {
const ns = useNamespace('action-toolbar');
// 按钮状态控制
const actionStates = reactive<{ [p: string]: ActionState }>({});
props.actionGroup.getPSUIActionGroupDetails()?.forEach(detail => {
const action = detail.getPSUIAction();
if (action) {
actionStates[action.uIActionTag] = { disabled: false };
}
});
// 点击事件抛给表格执行
const handleClick = async (
detail: IPSUIActionGroupDetail,
event: MouseEvent,
) => {
emit('action-click', detail, event);
};
return { ns, handleClick };
},
render() {
const details = this.actionGroup.getPSUIActionGroupDetails() || [];
return (
<div class={`${this.ns.b()}`}>
{details.length > 0 &&
details.map(detail => {
const action = detail.getPSUIAction();
if (action) {
return (
<i-button
type='text'
on-click={(e: MouseEvent) => this.handleClick(detail, e)}
disabled={this.actionStates[action.uIActionTag].disabled}
class={[this.ns.be('item'), this.ns.is('disabled', false)]}
>
{detail.showIcon && action.getPSSysImage() && (
<app-icon icon={action.getPSSysImage()}></app-icon>
)}
{detail.showCaption ? action!.caption : ''}
</i-button>
);
}
return null;
})}
</div>
);
},
});
import { computed, defineComponent, PropType } from 'vue';
import {
IPSFlexLayoutPos,
IPSGridLayoutPos,
IPSLayoutPos,
} from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
export const AppCol = defineComponent({
name: 'AppCol',
props: {
layoutPos: Object as PropType<IPSLayoutPos>,
modelData: {
type: Object as PropType<IData>,
required: true,
},
},
setup(props) {
const ns = useNamespace('col');
// 计算栅格布局的属性
const gridAttrs = computed(() => {
const gridLayoutPos = props.layoutPos as IPSGridLayoutPos;
// FLEX布局时不计算
if (gridLayoutPos.layout === 'FLEX') {
return {};
}
// 计算倍率,12列栅格为2
const {
colXS,
colXSOffset,
colSM,
colSMOffset,
colMD,
colMDOffset,
colLG,
colLGOffset,
} = gridLayoutPos;
const multiplier = gridLayoutPos.layout === 'TABLE_24COL' ? 1 : 2;
return {
xs: { span: colXS * multiplier, offset: colXSOffset * multiplier },
sm: { span: colSM * multiplier, offset: colSMOffset * multiplier },
md: { span: colMD * multiplier, offset: colMDOffset * multiplier },
lg: { span: colLG * multiplier, offset: colLGOffset * multiplier },
};
});
const cssVars = computed(() => {
const styles = {};
Object.assign(styles, {
width: props.modelData.width ? props.modelData.width : '100%',
height: props.modelData.height ? props.modelData.height : '100%',
});
return styles;
});
return { ns, gridAttrs, cssVars };
},
render(h) {
if (this.layoutPos?.layout === 'FLEX') {
return (
<div
class={[this.ns.b(), this.ns.m('flex')]}
style={{
flexGrow: (this.layoutPos as IPSFlexLayoutPos).grow,
...this.cssVars,
}}
>
{this.$slots.default}
</div>
);
}
return h(
'i-col',
{
class: [this.ns.b(), this.ns.m('grid')],
props: this.gridAttrs,
style: this.cssVars,
},
this.$slots.default,
);
},
});
import { defineComponent } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
export const AppGridEditItem = defineComponent({
name: 'AppGridEditItem',
props: {
required: {
type: Boolean,
},
error: {
type: String || null,
},
},
setup() {
const ns = useNamespace('grid-edit-item');
return { ns };
},
render() {
return (
<div class={this.ns.b()}>
{!this.error ? (
this.$slots.default
) : (
<tooltip
content={this.error}
transfer
transfer-class-name='grid-error'
placement='top'
style='border: 1px solid red'
>
{this.$slots.default}
</tooltip>
)}
</div>
);
},
});
export default AppGridEditItem;
import { defineComponent } from 'vue';
import '@/styles/components/common/app-grid-pagination/app-grid-pagination.scss';
import { useNamespace } from '@ibiz-template/vue-util';
export const AppGridPagination = defineComponent({
name: 'AppGridPagination',
props: {
total: {
type: Number,
},
curPage: {
type: Number,
},
size: {
type: Number,
},
},
setup() {
const ns = useNamespace('grid-page');
return { ns };
},
methods: {
onPageChange(page: number) {
this.$emit('change', page);
},
onPageSizeChange(size: number) {
this.$emit('page-size-change', size);
},
pageReset() {
this.$emit('page-reset');
},
},
render() {
return (
<div class={this.ns.b()}>
<page
transfer={true}
total={this.total}
show-sizer
show-elevator
current={this.curPage}
page-size={this.size}
page-size-opts={[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]}
show-total
on-on-change={this.onPageChange}
on-on-page-size-change={this.onPageSizeChange}
>
<span class={this.ns.b('btn')}>
<i-button
icon='md-refresh'
title='刷新'
on-click={this.pageReset}
></i-button>
</span>
<span>共计&nbsp;{this.total}&nbsp;条数据</span>
</page>
</div>
);
},
});
export default AppGridPagination;
import { IPSSysImage } from '@ibiz-template/model';
import { computed, defineComponent, PropType } from 'vue';
export const AppIcon = defineComponent({
name: 'AppIcon',
props: {
icon: {
type: Object as PropType<IPSSysImage>,
},
size: {
type: String as PropType<'small' | 'medium' | 'large'>,
},
},
setup(props) {
const classNames = computed(() => {
let className = ' app-icon';
if (props.size) {
className += ` app-icon${props.size}`;
}
return className;
});
const BaseUrl = `${ibiz.env.assetsUrl}/imgs/`;
return { classNames, BaseUrl };
},
render() {
if (this.icon) {
if (this.icon.cssClass) {
return <i class={this.icon.cssClass + this.classNames} />;
}
if (this.icon.imagePath) {
return (
<img
class={this.classNames}
src={this.BaseUrl + this.icon.imagePath}
/>
);
}
}
return null;
},
});
export default AppIcon;
<script>
export const patternTypes = [String, RegExp, Array];
export default {
name: 'AppKeepAlive',
props: {
// 根据组件 name 进行匹配。如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。
include: patternTypes,
exclude: patternTypes,
max: [String, Number],
keyList: [Array],
},
data() {
return {
// eslint-disable-next-line vue/no-reserved-keys
_toString: Object.prototype.toString,
};
},
watch: {
include(val) {
const _this = this;
_this.pruneCache(function (name) {
return _this.matches(val, name);
});
},
exclude(val) {
const _this = this;
_this.pruneCache(function (name) {
return !_this.matches(val, name);
});
},
keyList(val) {
const _this = this;
// 配置了keyList但是下方插件key
_this.pruneCache2(function (name) {
return !_this.matches(val, name);
});
},
},
created() {
// 保存缓存的组件
this.cache = Object.create(null);
// 保存缓存的组件的key
this.keys = [];
},
destroyed() {
const _this = this;
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const key in _this.cache) {
_this.pruneCacheEntry(_this.cache, key, _this.keys);
}
},
methods: {
pruneCacheEntry(cache, key, keys, _current) {
const cached = cache[key];
if (cached) {
cached.componentInstance.$destroy();
}
// eslint-disable-next-line no-param-reassign
cache[key] = null;
this.remove(keys, key);
},
pruneCache(filter) {
const _this = this;
const cache = _this.cache;
const keys = _this.keys;
const _vnode = _this._vnode;
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const key in cache) {
const cachedNode = cache[key];
if (cachedNode) {
const name = _this.getComponentName(cachedNode.componentOptions);
if (name && !filter(name)) {
_this.pruneCacheEntry(cache, key, keys, _vnode);
}
}
}
},
pruneCache2(filter) {
const _this = this;
const cache = _this.cache;
const keys = _this.keys;
const _vnode = _this._vnode;
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const key in cache) {
const cachedNode = cache[key];
if (cachedNode) {
const name = cachedNode.data.curPath;
if (name && filter(name)) {
_this.pruneCacheEntry(cache, key, keys, _vnode);
}
}
}
},
matches(pattern, name) {
if (Array.isArray(pattern)) {
return pattern.indexOf(name) > -1;
}
if (typeof pattern === 'string') {
return pattern.split(',').indexOf(name) > -1;
}
if (this.isRegExp(pattern)) {
return pattern.test(name);
}
/* istanbul ignore next */
return false;
},
getComponentName(opts) {
return opts && (opts.Ctor.options.name || opts.tag);
},
getFirstComponentChild(children) {
const _this = this;
if (Array.isArray(children)) {
for (let i = 0; i < children.length; i++) {
const c = children[i];
if (
_this.isDef(c) &&
(_this.isDef(c.componentOptions) || _this.isAsyncPlaceholder(c))
) {
return c;
}
}
}
},
isAsyncPlaceholder(node) {
return node.isComment && node.asyncFactory;
},
isDef(v) {
return v !== undefined && v !== null;
},
isRegExp(v) {
return this._toString.call(v) === '[object RegExp]';
},
remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1);
}
}
},
},
render: function render() {
// this.$slots.default 包含了所有没有被包含在具名插槽中的节点
// 这里取得第一个子组件
const _this = this;
const slot = _this.$slots.default;
const vnode = _this.getFirstComponentChild(slot);
const componentOptions = vnode && vnode.componentOptions;
if (componentOptions) {
// check pattern
const name = _this.getComponentName(componentOptions);
const ref = _this;
const include = ref.include;
const exclude = ref.exclude;
const keyList = ref.keyList;
// 获取第一个子组件上面的key
const slotKey = vnode.key;
// 如果 componentName 没有作为keep-alive被包含进来,直接返回
if (
// 包括并且不匹配的
(include && (!name || !_this.matches(include, name))) ||
// 排除并且匹配的
(exclude && name && _this.matches(exclude, name)) ||
// keyList中存在并且不匹配的
(keyList && !slotKey && !_this.matches(keyList, slotKey))
) {
return vnode;
}
const ref$1 = _this;
const cache = ref$1.cache;
const keys = ref$1.keys;
const key =
vnode.key == null
? // 相同的构造器(constructor)可能会注册为不同的本地组件,所以仅有一个 cid 是不够的
componentOptions.Ctor.cid +
(componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key;
if (cache[key]) {
// 如果已经缓存了,需要保持当前的key 是最新的
vnode.componentInstance = cache[key].componentInstance;
// make current key freshest
_this.remove(keys, key);
keys.push(key);
} else {
// 否则,进行缓存并更新keys数组。
cache[key] = vnode;
keys.push(key);
// 缓存组件超出最大值,将队首的组件销毁(先进先出)
if (_this.max && keys.length > parseInt(_this.max, 10)) {
_this.pruneCacheEntry(cache, keys[0], keys, _this._vnode);
}
}
vnode.data.keepAlive = true;
vnode.data.curPath = slotKey;
}
return vnode || (slot && slot[0]);
},
};
</script>
import { IPSFlexLayout, IPSLayout } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import { defineComponent, PropType } from 'vue';
export const AppRow = defineComponent({
name: 'AppRow',
props: {
layout: Object as PropType<IPSLayout>,
},
setup() {
const ns = useNamespace('row');
return { ns };
},
render() {
if (this.layout?.layout === 'FLEX') {
const { dir, align, vAlign } = this.layout as IPSFlexLayout;
return (
<div
class={[this.ns.b(), this.ns.m('flex')]}
style={`display:flex;flex-direction: ${dir};justify-content: ${align};align-items: ${vAlign};`}
>
{this.$slots.default}
</div>
);
}
return (
<row class={[this.ns.b(), this.ns.m('grid')]}>{this.$slots.default}</row>
);
},
});
<script setup lang="ts">
import { ref } from 'vue';
import '@/styles/components/common/app-transition/app-transition.scss';
const transName = ref('slide-right');
</script>
<template>
<Transition :name="transName" mode="out-in">
<slot></slot>
</Transition>
</template>
import { defineComponent } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/common/app-user/app-user.scss';
export const AppUser = defineComponent({
name: 'AppUser',
setup() {
const ns = useNamespace('app-user');
const { srfusername = '游客' } = ibiz.appData?.context || {};
return { ns, srfusername };
},
methods: {
async onClick() {
const res = await ibiz.auth.v7logout();
if (res.ok) {
console.log('登出成功');
this.$router.push(
`/login?ru=${encodeURIComponent(
window.location.hash.replace('#/', '/'),
)}`,
);
} else {
console.log('登出失败');
}
},
},
render() {
return (
<div class={this.ns.b()}>
<dropdown class={this.ns.b('avatar')}>
<span class={this.ns.b('avatar-wrapper')}>
<avatar
size='small'
src='https://i.loli.net/2017/08/21/599a521472424.jpg'
/>
<span class={this.ns.be('avatar', 'name')}>{this.srfusername}</span>
</span>
<dropdownMenu slot='list'>
<dropdownItem>
<i class='ivu-icon ivu-icon-ios-log-out'></i>
<span on-click={this.onClick}>退出登录</span>
</dropdownItem>
</dropdownMenu>
</dropdown>
</div>
);
},
});
import AppIcon from './app-icon/app-icon';
import QuickSearch from './quick-search/quick-search';
import { ViewToolbar } from './view-toolbar/view-toolbar';
import { AppCol } from './app-col/app-col';
import { AppRow } from './app-row/app-row';
import AppGridPagination from './app-grid-pagination/app-grid-pagination';
import AppGridEditItem from './app-grid-edit-item/app-grid-edit-item';
import { ActionToolbar } from './action-toolbar/action-toolbar';
import { AppUser } from './app-user/app-user';
import AppTransition from './app-transition/app-transition.vue';
export {
AppIcon,
ViewToolbar,
AppCol,
AppRow,
QuickSearch,
AppGridPagination,
AppGridEditItem,
ActionToolbar,
AppUser,
AppTransition,
};
import { computed, defineComponent, watch } from 'vue';
import { debounce } from 'lodash-es';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/common/quick-search/quick-search.scss';
export const QuickSearch = defineComponent({
name: 'QuickSearch',
props: {
value: {
type: String,
},
viewMode: {
type: String,
required: true,
},
},
setup(props, { emit }) {
const ns = useNamespace('quick-search');
const valu = computed({
get() {
return props.value;
},
set(val: string | undefined) {
emit('update', val!);
},
});
const search = debounce(() => {
emit('search');
}, 500);
const onChange = (e: InputEvent) => {
if (e.target) {
valu.value = (e.target as unknown as { value: string }).value;
}
};
watch(
() => props.value,
() => {
search();
},
);
return { ns, valu, search, onChange };
},
render() {
return (
<i-input
value={this.value}
class={[this.ns.b(), this.ns.m(this.viewMode.toLowerCase())]}
search={true}
on-on-change={this.onChange}
on-on-enter={this.search}
on-on-search={this.search}
></i-input>
);
},
});
export default QuickSearch;
import SkeletonCard from './skeleton-card/skeleton-card';
import SkeletonElement from './skeleton-element/skeleton-element';
import SkeletonInput from './skeleton-input/skeleton-input';
export { SkeletonCard, SkeletonElement, SkeletonInput };
import { useNamespace } from '@ibiz-template/vue-util';
import { defineComponent, onUnmounted, ref, watch } from 'vue';
import type { PropType } from 'vue';
import '@/styles/components/common/skeleton/skeleton-card/skeleton-card.scss';
export default defineComponent({
props: {
active: {
type: Boolean,
default: false,
},
round: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: true,
},
title: {
type: Boolean,
default: true,
},
paragraph: {
type: [Boolean, Object] as PropType<boolean | { rows: number }>,
default: true,
},
},
setup(props, { slots }) {
const ns = useNamespace('skeleton-card');
const content = ref<HTMLElement | null>(null);
const rows = ref<number>(2);
watch(
() => props.paragraph,
value => {
if (!(typeof value === 'boolean')) {
rows.value = Math.floor(value.rows > 2 ? value.rows : 2);
}
},
);
const childHeight = 16;
const childMarginBottom = 12;
let observer: ResizeObserver | null = null;
watch(
content,
() => {
if (content.value && !observer && ResizeObserver) {
observer = new ResizeObserver(entries => {
if (typeof props.paragraph === 'boolean') {
rows.value = Math.floor(
(entries[0].contentRect.height + childMarginBottom) /
(childHeight + childMarginBottom),
);
}
});
observer.observe(content.value);
} else if (!content.value && observer) {
observer.disconnect();
observer = null;
}
},
{ immediate: true },
);
onUnmounted(() => {
if (observer) {
observer.disconnect();
observer = null;
}
});
return () => (
<div
class={`${props.loading ? ns.b() : ''} ${
props.loading && props.active ? ns.m('active') : ''
} ${props.loading && props.round ? ns.m('round') : ''}`.trim()}
>
{props.loading && props.title ? (
<header class={ns.b('header')}>
<h3 class={ns.be('header', 'title')}></h3>
</header>
) : null}
{props.loading && props.paragraph ? (
<main class={ns.b('content')} ref={content as unknown as string}>
{Array.from({ length: rows.value }).map((_value, i) => (
<div class={ns.be('content', 'item')} key={i}></div>
))}
</main>
) : null}
{typeof slots.default === 'function' && !props.loading
? slots.default()
: null}
</div>
);
},
});
import { computed, defineComponent } from 'vue';
import type { PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/common/skeleton/skeleton-element/skeleton-element.scss';
export default defineComponent({
props: {
prefixClass: String,
size: [String, Number] as PropType<
'large' | 'medium' | 'small' | 'default' | number
>,
shape: String as PropType<'circle' | 'square' | 'round' | 'default'>,
active: Boolean,
},
setup(props) {
const ns = useNamespace(`skeleton-${props.prefixClass}`);
const sizeClass = computed(() => {
return typeof props.size === 'string' && props.size !== 'default'
? ns.m(props.size)
: '';
});
const shapeClass = computed(() => {
return props.shape && props.shape !== 'default' ? ns.m(props.shape) : '';
});
const sizeStyle = computed(() => {
return typeof props.size === 'number'
? {
width: `${props.size}px`,
height: `${props.size}px`,
lineHeight: `${props.size}px`,
}
: {};
});
return () => (
<span
class={`${ns.b()} ${sizeClass.value} ${shapeClass.value}`.trim()}
style={sizeStyle.value}
></span>
);
},
});
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
export default defineComponent({
props: {
size: {
type: String as PropType<'large' | 'medium' | 'small' | 'default'>,
default: 'default',
},
active: Boolean,
},
setup(props) {
const ns = useNamespace('skeleton-element');
return () => (
<div class={`${ns.b()} ${props.active ? ns.m('active') : ''}`.trim()}>
<skeleton-element prefixClass='input' props={props}></skeleton-element>
</div>
);
},
});
import { computed, defineComponent, onMounted, ref } from 'vue';
import {
IPSDETBGroupItem,
IPSDETBUIActionItem,
IPSDEToolbarItem,
ToolbarModel,
} from '@ibiz-template/model';
import { ToolbarNeuron } from '@ibiz-template/controller';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/common/view-toolbar/view-toolbar.scss';
const btnContent = (item: IPSDEToolbarItem, viewMode: string) => {
const image = item.getPSSysImage();
if (viewMode === 'EMBED') {
if (image) {
return [<app-icon icon={image} />, item.caption];
}
return [<img src='undefined' />, item.caption];
}
return [<app-icon icon={image} />, item.caption];
};
export const ViewToolbar = defineComponent({
name: 'ViewToolbar',
props: {
modelData: {
type: ToolbarModel,
required: true,
},
viewMode: {
type: String,
required: true,
},
},
setup(props, { emit }) {
const ns = useNamespace('view-toolbar');
const neuron = new ToolbarNeuron({});
emit('neuronInit', neuron);
// 正在执行的工具栏项
const doingToolbarItem = ref<string>('');
onMounted(async () => {
await neuron.evt.asyncEmit('mounted');
});
const handleClick = async (item: IPSDEToolbarItem, _event: MouseEvent) => {
doingToolbarItem.value = item.id;
try {
await neuron.evt.asyncEmit(
'itemClick',
item as IPSDETBUIActionItem,
_event,
);
} finally {
doingToolbarItem.value = '';
}
};
const btnSize = computed(() => {
return props.viewMode === 'EMBED' ? 'small' : 'default';
});
return { ns, doingToolbarItem, handleClick, btnSize };
},
render() {
return (
<div class={[this.ns.b(), this.ns.m(this.viewMode.toLowerCase())]}>
{this.modelData!.source.getPSDEToolbarItems()?.map(item => {
if (item.itemType === 'SEPERATOR') {
return (
<div key={item.id} class={this.ns.b('item')}>
|
</div>
);
}
if (item.itemType === 'RAWITEM') {
return (
<div key={item.id} class={this.ns.b('item')}>
{btnContent(item, this.viewMode)}
</div>
);
}
if (item.itemType === 'DEUIACTION') {
return (
<div key={item.id} class={this.ns.b('item')}>
<i-button
title={item.tooltip}
size={this.btnSize}
loading={
!!this.doingToolbarItem && this.doingToolbarItem === item.id
}
disabled={!!this.doingToolbarItem}
on-click={(e: MouseEvent) => this.handleClick(item, e)}
>
{btnContent(item, this.viewMode)}
</i-button>
</div>
);
}
if (item.itemType === 'ITEMS') {
return (
<div key={item.id} class={this.ns.b('item')}>
<dropdown trigger='click' placement='bottom-start'>
<i-button title={item.tooltip} size={this.btnSize}>
{btnContent(item, this.viewMode)}
</i-button>
<dropdown-menu slot='list'>
{(item as IPSDETBGroupItem)
.getPSDEToolbarItems()!
.map(item2 => {
return (
<dropdown-item
key={item2.id}
nativeOn-click={(e: MouseEvent) =>
this.handleClick(item2, e)
}
>
{btnContent(item2, this.viewMode)}
</dropdown-item>
);
})}
</dropdown-menu>
</dropdown>
</div>
);
}
return null;
})}
</div>
);
},
});
import { defineComponent, ref, watch } from 'vue';
import type { PropType } from 'vue';
import { TextBoxEditorController } from '@ibiz-template/controller';
import { useNamespace } from '@ibiz-template/vue-util';
export default defineComponent({
props: {
value: Number,
controller: {
type: TextBoxEditorController,
required: true,
},
data: {
type: Object as PropType<IData>,
required: true,
},
},
emits: {
change: (_value: number | null) => true,
},
setup(props, { emit }) {
const ns = useNamespace('app-input-number');
const c = props.controller;
const currentVal = ref<number | null>(null);
watch(
() => props.value,
(newVal, oldVal) => {
if (newVal !== oldVal) {
currentVal.value = newVal!;
}
},
{ immediate: true },
);
const handleChange = (e: number | null) => {
emit('change', e);
};
return {
ns,
c,
currentVal,
handleChange,
};
},
render() {
return (
<div class={this.ns.b()}>
<input-number
value={this.currentVal}
placeholder={this.c.placeHolder}
readonly={this.c.model.readOnly}
precision={this.c.model.precision}
on-on-change={this.handleChange}
></input-number>
</div>
);
},
});
import { PickerEditorController } from '@ibiz-template/controller';
import { defineComponent, PropType, ref, Ref, watch } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
export const AppPickerLinkOnly = defineComponent({
name: 'AppPickerLinkOnly',
props: {
value: {
type: String,
},
controller: {
type: PickerEditorController,
},
data: {
type: Object as PropType<IData>,
},
},
emits: {
change: (_value: Array<string | number> | string, _tag?: string) => true,
},
setup(props, { emit }) {
const ns = useNamespace('app-picker-linkonly');
const c = props.controller!;
const curValue: Ref<string> = ref('');
watch(
() => props.value,
(newVal, oldVal) => {
if (newVal && newVal !== oldVal) {
curValue.value = `${newVal}`;
}
},
{ immediate: true },
);
// 处理视图关闭,往外抛值
const handleOpenViewClose = (result: IData[]) => {
const item: IData = {};
if (result && Array.isArray(result)) {
Object.assign(item, result[0]);
}
if (c.valueItem) {
emit('change', item[c.keyName], c.valueItem);
}
emit('change', item[c.textName]);
};
// 打开数据链接视图
const openLinkView = async () => {
const res = await c.openLinkView(props.data!);
if (res) {
handleOpenViewClose(res);
}
};
return { ns, openLinkView, curValue };
},
render() {
return (
<div class={this.ns.b()}>
<a on-click={this.openLinkView}>{this.curValue}</a>
</div>
);
},
});
export default AppPickerLinkOnly;
import { Ref, ref, watch, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
export const AppSelectTree = defineComponent({
name: 'AppSelectTree',
props: {
value: {
type: String as PropType<Array<string> | string>,
},
multiple: {
type: Boolean,
},
nodesData: {
type: Array as PropType<IData[]>,
},
},
setup(props) {
const ns = useNamespace('app-select-tree');
const visible = ref(false);
const treeNodes: Ref<IData[]> = ref([]);
const handletreeNodes = (nodes: readonly IData[]) => {
if (nodes.length === 0) {
return [];
}
const list: IData[] = [];
nodes.forEach((codeItem: IData) => {
const tempObj: IData = {
title: codeItem.text,
value: codeItem.value,
children: [],
};
if (codeItem.children && codeItem.children.length > 0) {
tempObj.children = handletreeNodes(codeItem.children);
}
list.push(tempObj);
});
return list;
};
watch(
() => props.nodesData,
(newVal, oldVal) => {
if (newVal !== oldVal) {
treeNodes.value = handletreeNodes(newVal!);
}
},
{ immediate: true },
);
const triggerMenu = (_visible: boolean) => {
if (!_visible) {
visible.value = !visible.value;
} else {
visible.value = _visible;
}
};
return { ns, visible, treeNodes, triggerMenu };
},
render() {
return (
<div class={this.ns.b()}>
<dropdown
visible={this.visible}
trigger='custom'
transfer-class-name={this.ns.e('dropdown')}
transfer
on-on-clickoutside={() => {
this.triggerMenu(false);
}}
>
<div
on-click={() => {
this.triggerMenu(true);
}}
>
打开下拉树
</div>
<dropdownMenu slot='list'>
<tree data={this.treeNodes} show-checkbox></tree>
</dropdownMenu>
</dropdown>
</div>
);
},
});
export default AppSelectTree;
import { computed, defineComponent, ref } from 'vue';
import type { PropType } from 'vue';
import { CheckBoxListEditorController } from '@ibiz-template/controller';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-check-box-list/ibiz-check-box-list.scss';
export default defineComponent({
props: {
value: String,
controller: {
type: CheckBoxListEditorController,
required: true,
},
data: {
type: Object as PropType<IData>,
required: true,
},
},
emits: {
change: (_value: null | string | number | string[]) => true,
},
setup(props, { emit }) {
const ns = useNamespace('check-box-list');
const c = props.controller;
const editorModel = c.model;
const codeList = editorModel.codeList;
// 代码表数据
const items = ref<readonly IData[]>([]);
c.loadCodeList(props.data).then(_codeList => {
items.value = _codeList;
});
// 当前模式
const currentMode = computed(() => {
if (codeList && codeList.orMode) {
return codeList.orMode.toLowerCase();
}
return 'str';
});
// 值分隔符
let valueSeparator = ',';
if (codeList && codeList.valueSeparator) {
valueSeparator = codeList.valueSeparator;
}
// 选中数组
const selectArray = computed({
get() {
if (props.value) {
if (Object.is(currentMode.value, 'num') && items) {
const selectsArray: Array<string | number> = [];
const num: number = parseInt(props.value, 10);
items.value.forEach((item: IData) => {
// eslint-disable-next-line no-bitwise
if ((num & item.value) === item.value) {
selectsArray.push(item.value);
}
});
return selectsArray;
}
if (Object.is(currentMode.value, 'str')) {
if (props.value !== '') {
if (codeList) {
const selects: Array<string | number> =
props.value.split(valueSeparator);
if (codeList.codeItemValueNumber) {
for (let i = 0, len = selects.length; i < len; i++) {
selects[i] = Number(selects[i]);
}
}
return selects;
}
return props.value.split(',');
}
}
}
return [];
},
set(val: Array<string | number>) {
let value: null | string | number | string[] = null;
if (Object.is(currentMode.value, 'num')) {
let temp: number = 0;
val.forEach((item: unknown) => {
if (typeof item === 'string') {
// eslint-disable-next-line no-bitwise
temp |= parseInt(item, 10);
}
});
value = temp;
} else if (Object.is(currentMode.value, 'str')) {
const _datas: string[] = [];
if (items.value.length > 0) {
items.value.forEach((_item: IData) => {
const index = val.findIndex((_key: unknown) =>
Object.is(_item.value, _key),
);
if (index === -1) {
return;
}
_datas.push(_item.value);
});
value = _datas.join(valueSeparator);
}
}
emit('change', value);
},
});
const onSelectArrayChange = (value: Array<string | number>) => {
selectArray.value = value;
};
return {
ns,
items,
selectArray,
onSelectArrayChange,
};
},
render() {
return (
<div class={this.ns.b()}>
<checkbox-group
value={this.selectArray}
on-on-change={this.onSelectArrayChange}
>
{this.items.map((_item, index: number) => (
<checkbox
key={index}
label={_item.value}
readonly={this.controller.model.readOnly}
>
<span class={this.ns.e('text')}>{_item.text}</span>
</checkbox>
))}
</checkbox-group>
</div>
);
},
});
import { DatePickerEditorController } from '@ibiz-template/controller';
import { ref, watch, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-date-picker/ibiz-date-picker.scss';
export const IBizDatePicker = defineComponent({
name: 'IBizDatePicker',
props: {
value: {
type: String,
},
controller: {
type: DatePickerEditorController,
},
data: {
type: Object as PropType<IData>,
},
disable: {
type: Boolean,
},
},
emits: {
change: (_value: string) => true,
},
setup(props, { emit }) {
const ns = useNamespace('date-picker');
const c = props.controller;
const editorModel = c!.model;
const type = ref('date');
const format = ref('yyyy-MM-dd');
switch (editorModel.editorType) {
// 时间选择器
case 'DATEPICKER':
type.value = 'datetime';
format.value = 'yyyy-MM-dd HH:mm:ss';
break;
// 时间选择控件
case 'DATEPICKEREX':
type.value = 'date';
format.value = 'yyyy-MM-dd HH:mm:ss';
break;
// 时间选择控件_无小时
case 'DATEPICKEREX_NOTIME':
type.value = 'date';
format.value = 'yyyy-MM-dd';
break;
// 时间选择控件_小时
case 'DATEPICKEREX_HOUR':
type.value = 'datetime';
format.value = 'yyyy-MM-dd HH';
break;
// 时间选择控件_分钟
case 'DATEPICKEREX_MINUTE':
type.value = 'datetime';
format.value = 'yyyy-MM-dd HH:mm';
break;
// 时间选择控件_秒钟
case 'DATEPICKEREX_SECOND':
type.value = 'datetime';
format.value = 'yyyy-MM-dd HH:mm:ss';
break;
// 时间选择控件_无日期
case 'DATEPICKEREX_NODAY':
type.value = 'datetime';
format.value = 'HH:mm:ss';
break;
// 时间选择控件_无日期无秒钟
case 'DATEPICKEREX_NODAY_NOSECOND':
type.value = 'datetime';
format.value = 'HH:mm';
break;
// 时间选择控件_无秒钟
case 'DATEPICKEREX_NOSECOND':
type.value = 'datetime';
format.value = 'yyyy-MM-dd HH:mm';
break;
default:
break;
}
// 值格式化
const valueFormat = c!.valueFormat;
if (valueFormat) {
const tempFormat: string = valueFormat
.replace('YYYY', 'yyyy')
.replace('DD', 'dd');
format.value = tempFormat;
}
const currentVal = ref('');
watch(
() => props.value,
(newVal, oldVal) => {
if (newVal && newVal !== oldVal) {
currentVal.value = newVal;
}
},
{ immediate: true },
);
// 处理值变更
const handleChange = (date: string, _dateType: IData) => {
emit('change', date);
};
return { ns, c, editorModel, type, format, currentVal, handleChange };
},
render() {
return (
<div class={this.ns.b()}>
<datePicker
type={this.type}
format={this.format}
placeholder={this.c!.placeHolder}
value={this.currentVal}
readonly={this.c!.model.readOnly}
on-on-change={this.handleChange}
disabled={this.disable}
></datePicker>
</div>
);
},
});
export default IBizDatePicker;
import { DropDownListEditorController } from '@ibiz-template/controller';
import { watch, ref, Ref, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-dropdown-list/ibiz-dropdown-list.scss';
export const IBizDropDownList = defineComponent({
name: 'IBizDropDownList',
props: {
value: {
type: String,
},
controller: {
type: DropDownListEditorController,
},
data: {
type: Object as PropType<IData>,
},
},
emits: {
change: (_value: string | Array<string>) => true,
},
setup(props, { emit }) {
const ns = useNamespace('dropdown-list');
const c = props.controller;
// 是否是树形
const hasChildren = ref(false);
// 当前值
const curValue: Ref<Array<string> | string | undefined> = ref('');
watch(
() => props.value,
(newVal, oldVal) => {
if (newVal && newVal !== oldVal) {
curValue.value = c!.multiple ? props.value?.split(',') : props.value!;
}
},
{ immediate: true },
);
// 代码表
const items: Ref<readonly IData[]> = ref([]);
c!.loadCodeList(props.data!).then(codeList => {
items.value = codeList;
for (let i = 0; i < codeList.length; i++) {
const _item = codeList[i];
if (_item.children) {
hasChildren.value = true;
break;
}
}
});
// 值变更
const onChange = (select: string | Array<string>) => {
const value =
Object.prototype.toString.call(select) === '[object Array]'
? (select as Array<string>).join(',')
: select;
emit('change', value);
};
return { ns, c, curValue, onChange, items, hasChildren };
},
render() {
return (
<div class={this.ns.b()}>
{this.hasChildren ? (
<div class={this.ns.e('tree-select')}>
<app-select-tree
value={this.curValue}
nodes-data={this.items}
multiple={this.c!.multiple}
></app-select-tree>
</div>
) : (
<div class={this.ns.e('select')}>
<i-select
value={this.curValue}
allow-clear
class={this.ns.e('dropdown-list')}
multiple={this.c!.multiple}
placeholder={this.c!.placeHolder}
on-on-change={this.onChange}
>
{this.items.map(item => {
return <i-option value={item.value}>{item.text}</i-option>;
})}
</i-select>
</div>
)}
</div>
);
},
});
export default IBizDropDownList;
import { UploadEditorController } from '@ibiz-template/controller';
import { getCookie } from 'qx-util';
import { onMounted, ref, Ref, watch, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-file-upload/ibiz-file-upload.scss';
export const IBizFileUpload = defineComponent({
name: 'IBizFileUpload',
props: {
value: {
type: String,
},
controller: {
type: UploadEditorController,
require: true,
},
data: {
type: Object as PropType<IData>,
},
},
emits: {
change: (_value: string | null) => true,
},
setup(props, { emit }) {
const ns = useNamespace('file-upload');
const c = props.controller as UploadEditorController;
// 文件列表
const files: Ref<IData[]> = ref([]);
// 请求头
const headers: Ref<IData> = ref({});
// 上传文件路径
const uploadUrl: Ref<string> = ref(ibiz.env.baseUrl + ibiz.env.UploadFile);
// 下载文件路径
const downloadUrl: Ref<string> = ref(ibiz.env.ExportFile);
// 设置files
const setFiles = (value: string | null | undefined) => {
if (value === null) {
files.value = [];
}
if (!value) {
return;
}
const _files = JSON.parse(value);
if (
value &&
Object.prototype.toString.call(_files) === '[object Array]'
) {
files.value = _files;
} else {
files.value = [];
}
};
// 文件上传缓存对象
const uploadCache: IData = {
count: 0,
cacheFiles: [],
};
// 处理参数
const dataProcess = () => {
let _url = ibiz.env.baseUrl + ibiz.env.UploadFile;
if (c.upload_params.length > 0) {
_url += '?';
c.upload_params.forEach((item: IData, i: number) => {
_url += `${Object.keys(item)[0]}=${Object.values(item)[0]}`;
if (i < c.upload_params.length - 1) {
_url += '&';
}
});
}
uploadUrl.value = _url;
files.value.forEach((file: IData) => {
let url = `${downloadUrl.value}/${file.id}`;
if (c.export_params.length > 0) {
url += '?';
c.export_params.forEach((item: IData, i: number) => {
url += `${Object.keys(item)[0]}=${Object.values(item)[0]}`;
if (i < c.export_params.length - 1) {
url += '&';
}
});
}
// eslint-disable-next-line no-param-reassign
file.url = url;
});
};
// 设置头
const setHeaders = () => {
if (getCookie('access_token')) {
headers.value.Authorization = `Bearer ${getCookie('access_token')}`;
}
};
setHeaders();
watch(
() => props.value,
newVal => {
setFiles(newVal);
dataProcess();
},
{ immediate: true },
);
watch(
() => props.data,
newVal => {
c.getParams(newVal!);
},
{ immediate: true, deep: true },
);
onMounted(() => {
c.getParams(props.data!);
setFiles(props.value);
dataProcess();
});
// 上传前回调
const beforeUpload = () => {
uploadCache.count += 1;
};
// 上传成功回调
const onSuccess = (response: IData) => {
if (!response) {
return;
}
// 处理回调数据,并缓存
const arr: IData[] = [];
if (response?.length > 0) {
for (let index = 0; index < response.length; index++) {
const file = response[index];
arr.push({ name: file.filename, id: file.fileid });
}
} else {
arr.push({ name: response.filename, id: response.fileid });
}
uploadCache.cacheFiles.push(arr);
uploadCache.count -= 1;
// 回调都结束后的处理
if (uploadCache.count === 0) {
const result: IData[] = [];
// 添加已有的文件数据
files.value.forEach((_file: IData) => {
result.push({ name: _file.name, id: _file.id });
});
// 添加缓存的文件数据
uploadCache.cacheFiles.forEach((item: IData[]) => {
result.push(...item);
});
// 抛出值变更事件
const value: string | null =
result.length > 0 ? JSON.stringify(result) : null;
emit('change', value);
// 清空缓存的文件数据
uploadCache.cacheFiles = [];
}
};
// 上传失败回调
const onError = (error: IData) => {
uploadCache.count -= 1;
console.log(error);
};
// 删除回调
const onRemove = (file: IData, fileList: IData[]) => {
const arr: Array<IData> = [];
fileList.forEach((f: IData) => {
if (f.id !== file.id) {
arr.push({ name: f.name, id: f.id });
}
});
const value: string | null = arr.length > 0 ? JSON.stringify(arr) : null;
emit('change', value);
};
// 计算文件mime类型
const calcFilemime = (filetype: string) => {
let mime = 'image/png';
switch (filetype) {
case '.wps':
mime = 'application/kswps';
break;
case '.doc':
mime = 'application/msword';
break;
case '.docx':
mime =
'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
break;
case '.txt':
mime = 'text/plain';
break;
case '.zip':
mime = 'application/zip';
break;
case '.png':
mime = 'image/png';
break;
case '.gif':
mime = 'image/gif';
break;
case '.jpeg':
mime = 'image/jpeg';
break;
case '.jpg':
mime = 'image/jpeg';
break;
case '.rtf':
mime = 'application/rtf';
break;
case '.avi':
mime = 'video/x-msvideo';
break;
case '.gz':
mime = 'application/x-gzip';
break;
case '.tar':
mime = 'application/x-tar';
break;
default:
mime = 'image/png';
}
return mime;
};
/**
* 下载文件
*/
const DownloadFile = (url: string, file: IData) => {
// 发送get请求
ibiz.net
.get(
url,
{},
{},
{
responseType: 'blob',
},
)
.then((response: IData) => {
if (!response || response.status !== 200) {
console.log('error');
return;
}
// 请求成功,后台返回的是一个文件流
if (response.data) {
// 获取文件名
const filename = file.name;
const ext = `.${filename.split('.').pop()}`;
const filetype = calcFilemime(ext);
// 用blob对象获取文件流
const blob = new Blob([response.data], { type: filetype });
// 通过文件流创建下载链接
const href = URL.createObjectURL(blob);
// 创建一个a元素并设置相关属性
const a = document.createElement('a');
a.href = href;
a.download = filename;
// 添加a元素到当前网页
document.body.appendChild(a);
// 触发a元素的点击事件,实现下载
a.click();
// 从当前网页移除a元素
document.body.removeChild(a);
// 释放blob对象
URL.revokeObjectURL(href);
} else {
console.log('error');
}
})
.catch(error => {
console.error(error);
});
};
// 下载文件
const onDownload = (file: IData) => {
const url = `${downloadUrl.value}/${file.id}`;
DownloadFile(url, file);
};
return {
ns,
c,
uploadUrl,
headers,
files,
onDownload,
onError,
onRemove,
onSuccess,
beforeUpload,
};
},
render(h) {
return (
<div class={this.ns.b()}>
{h(
'upload',
{
ref: 'fileUpload',
props: {
action: this.uploadUrl,
headers: this.headers,
'default-file-list': this.files,
multiple: this.c.multiple,
type: this.c.isDrag ? 'drag' : 'select',
accept: this.c.accept,
'before-upload': this.beforeUpload,
'on-success': this.onSuccess,
'on-error': this.onError,
'on-remove': this.onRemove,
'on-preview': this.onDownload,
},
},
[
<i-button
icon='ios-cloud-upload-outline'
class={this.ns.b('button')}
>
上传文件
</i-button>,
],
)}
</div>
);
},
});
export default IBizFileUpload;
import { computed, defineComponent, ref, watch } from 'vue';
import type { PropType } from 'vue';
import { TextBoxEditorController } from '@ibiz-template/controller';
import { debounce } from 'lodash-es';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-input-box/ibiz-input-box.scss';
export default defineComponent({
props: {
value: [String, Number],
controller: {
type: TextBoxEditorController,
required: true,
},
data: {
type: Object as PropType<IData>,
required: true,
},
disable: {
type: Boolean,
},
},
emits: {
change: (_value: string) => true,
},
setup(props, { emit }) {
const ns = useNamespace('input-box');
const c = props.controller;
const editorModel = c.model;
// 文本域默认行数,仅在 textarea 类型下有效
const rows = ref(2);
if (editorModel.editorType === 'TEXTAREA_10') {
rows.value = 10;
}
// 类型
const type = computed(() => {
switch (editorModel.editorType) {
case 'TEXTBOX':
return 'text';
case 'PASSWORD':
return 'password';
case 'TEXTAREA':
case 'TEXTAREA_10':
return 'textarea';
default:
return 'string';
}
});
const currentVal = ref<string | number>('');
watch(
() => props.value,
(newVal, oldVal) => {
if (newVal !== oldVal) {
if (newVal === null) {
currentVal.value = '';
} else {
currentVal.value = newVal as string | number;
}
}
},
{ immediate: true },
);
// 值变更
const handleChange = debounce((e: IData) => {
emit('change', e.target.value);
}, 500);
return {
ns,
rows,
type,
currentVal,
handleChange,
};
},
render() {
return (
<div
class={`${this.ns.b()} ${this.ns.is(
'textarea',
Object.is(this.type, 'textarea'),
)}`}
>
<i-input
value={this.currentVal}
placeholder={this.controller.placeHolder}
readonly={this.controller.model.readOnly}
type={this.type}
rows={this.rows}
on-on-change={this.handleChange}
class={this.ns.b('input')}
disabled={this.disable}
></i-input>
</div>
);
},
});
import { PickerEditorController } from '@ibiz-template/controller';
import { ref, watch, Ref, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-mpicker/ibiz-mpicker.scss';
export const IBizMPicker = defineComponent({
name: 'IBizMPicker',
props: {
value: {
type: String,
},
controller: {
type: PickerEditorController,
},
data: {
type: Object as PropType<IData>,
},
},
emits: {
change: (_value: Array<string> | string | null, _tag?: string) => true,
},
setup(props, { emit }) {
const ns = useNamespace('mpicker');
const c = props.controller!;
// 当前表单项绑定值key的集合
const curValue: Ref<Array<string>> = ref([]);
// 实体数据集
const items: Ref<IData[]> = ref([]);
// 选中项key-value键值对
const selectItems: Ref<IData[]> = ref([]);
// 下拉是否打开
const open = ref(false);
// 加载中
const loading: Ref<boolean> = ref(false);
// 格式化值,把实体属性名格式化成srfkey和srfmajortext
const formatValue = (value: IData[]) => {
let result: IData[] = [];
if (c.keyName !== 'srfkey' || c.textName !== 'srfmajortext') {
value.forEach((item: IData) => {
result.push({
srfmajortext: item[c.textName] || item.srfmajortext,
srfkey: item[c.keyName],
});
});
} else {
result = value;
}
return result;
};
// 解析值,把srfkey和srfmajortext解析成实体属性名
const parseValue = (value: IData[]) => {
let result: IData[] = [];
if (c.keyName !== 'srfkey' || c.textName !== 'srfmajortext') {
value.forEach((item: IData) => {
result.push({
[c.textName]: item.srfmajortext,
[c.keyName]: item.srfkey,
});
});
} else {
result = value;
}
return result;
};
// 监听传入值
watch(
() => props.value,
newVal => {
curValue.value = [];
selectItems.value = [];
if (newVal) {
try {
selectItems.value = parseValue(JSON.parse(newVal));
selectItems.value.forEach((item: IData) => {
curValue.value.push(item[c.keyName]);
const index = items.value.findIndex(i =>
Object.is(i[c.keyName], item[c.keyName]),
);
if (index < 0) {
items.value.push({
[c.keyName]: item[c.keyName],
[c.textName]: item[c.textName] || item.srfmajortext,
});
}
});
} catch (error) {
if ((error as IData).name === 'SyntaxError') {
const srfkeys: string[] = newVal.split(',');
let srfmajortexts: string[] = [];
if (c.valueItem && props.data![c.valueItem]) {
srfmajortexts = props.data![c.valueItem].split(',');
}
if (
srfkeys &&
srfkeys.length > 0 &&
srfmajortexts &&
srfmajortexts.length > 0 &&
srfkeys.length === srfmajortexts.length
) {
srfkeys.forEach((id: string, index: number) => {
curValue.value.push(id);
selectItems.value.push({
[c.keyName]: id,
[c.textName]: srfmajortexts[index],
});
const _index = items.value.findIndex(i =>
Object.is(i[c.keyName], id),
);
if (_index < 0) {
items.value.push({
[c.keyName]: id,
[c.textName]: srfmajortexts[index],
});
}
});
}
}
}
}
},
{ immediate: true, deep: true },
);
// 处理视图关闭,往外抛值
const handleOpenViewClose = (result: IData[]) => {
const selects: IData[] = [];
if (result && Array.isArray(result)) {
result.forEach((select: IData) => {
selects.push({
[c.keyName]: select[c.keyName] || select.srfkey,
[c.textName]: select[c.textName] || select.srfmajortext,
});
const index = items.value.findIndex(item =>
Object.is(item[c.keyName], select[c.keyName]),
);
if (index < 0) {
items.value.push({
[c.textName]: select[c.textName] || select.srfmajortext,
[c.keyName]: select[c.keyName],
});
}
});
}
if (props.data) {
const value =
selects.length > 0 ? JSON.stringify(formatValue(selects)) : '';
emit('change', value);
}
};
// 打开数据选择视图
const openPickUpView = async () => {
// TODO 后续参数
// const _selectItems: IData[] = JSON.parse(JSON.stringify(selectItems.value));
// if (!Object.is(c.keyName, 'srfkey')) {
// _selectItems.forEach((item: IData, index: number) => {
// _selectItems[index].srfkey = item[c.keyName];
// });
// }
// if (!Object.is(c.textName, 'srfmajortext')) {
// _selectItems.forEach((item: IData, index: number) => {
// _selectItems[index].srfmajortext = item[c.textName] || item.srfmajortext;
// });
// }
// Object.assign(c.context, {
// srfparentdata: { srfparentkey: props.data[c.keyName] },
// });
// Object.assign(c.params, { selectedData: [..._selectItems] });
const res = await c.openPickUpView(props.data!);
if (res) {
handleOpenViewClose(res);
}
};
// 下拉选中回调
const onSelect = (selects: string[]) => {
const val: Array<IData> = [];
if (selects.length > 0) {
selects.forEach((select: string) => {
let index = items.value.findIndex(item =>
Object.is(item[c.keyName], select),
);
if (index >= 0) {
const item = items.value[index];
val.push({
[c.keyName]: item[c.keyName],
[c.textName]: item[c.textName] || item.srfmajortext,
});
} else {
index = selectItems.value.findIndex((item: IData) =>
Object.is(item[c.keyName], select),
);
if (index >= 0) {
const item = selectItems.value[index];
val.push(item);
}
}
});
const value = val.length > 0 ? JSON.stringify(formatValue(val)) : '';
emit('change', value);
} else {
emit('change', '');
}
};
// 搜索
const onSearch = async (query: string) => {
console.log('query', query);
if (c.model.appDataEntity) {
loading.value = true;
try {
const res = await c.getServiceData(query, props.data!);
loading.value = false;
if (res) {
items.value = res.data as IData[];
}
} catch (error) {
loading.value = false;
}
}
};
// 下拉打开
const onSelectOpen = (flag: boolean) => {
open.value = flag;
if (open.value) {
items.value = [];
onSearch('');
}
};
return {
ns,
c,
curValue,
loading,
items,
onSearch,
onSelectOpen,
onSelect,
openPickUpView,
};
},
render() {
return (
<div class={this.ns.b()}>
<i-select
value={this.curValue}
filterable
multiple
loading={this.loading}
placeholder={this.c.placeHolder}
remote-method={this.onSearch}
on-on-open-change={this.onSelectOpen}
on-on-change={this.onSelect}
>
{this.items.map((item, index) => {
return (
<i-option key={index} value={item[this.c.keyName]}>
{item[this.c.textName]}
</i-option>
);
})}
</i-select>
<div class={this.ns.e('btns')}>
{this.c.pickupView ? (
<i-button
icon='ios-search'
on-click={this.openPickUpView}
></i-button>
) : null}
</div>
</div>
);
},
});
export default IBizMPicker;
import { PickerEditorController } from '@ibiz-template/controller';
import { ref, Ref, watch, computed, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-picker-dropdown/ibiz-picker-dropdown.scss';
import { debounce, isEmpty } from 'lodash-es';
export const IBizPickerDropdown = defineComponent({
name: 'IBizPickerDropdown',
props: {
value: {
type: String,
},
controller: {
type: PickerEditorController,
},
data: {
type: Object as PropType<IData>,
required: true,
},
},
emits: {
change: (_value?: string | Array<string> | IData | null, _tag?: string) =>
true,
},
setup(props, { emit }) {
const ns = useNamespace('picker-dropdown');
const c = props.controller as PickerEditorController;
// 允许搜索
const allowSearch = ref(true);
// 当前值
const curValue: Ref<Array<string> | string | null> = ref('');
// 实体数据集
const items: Ref<IData[]> = ref([]);
// 是否显示所有数据
const isShowAll = ref(true);
watch(
() => props.value,
newVal => {
if (newVal || newVal === null) {
curValue.value = newVal;
const value = props.data[c.valueItem];
const index = items.value.findIndex((item: IData) =>
Object.is(item[c.keyName], value),
);
if (index !== -1) {
return;
}
// items里匹配不到当前值项值时,生成自身的选项
items.value = [];
if (!isEmpty(newVal) && !isEmpty(value)) {
items.value.push({ [c.textName]: newVal, [c.keyName]: value });
}
}
},
{ immediate: true },
);
// 获取关联数据项值
const refValue = computed(() => {
if (c.valueItem && props.data) {
const key = props.data[c.valueItem];
if (key) {
return key;
}
return '';
}
return curValue;
});
// 下拉是否打开
const open = ref(false);
// 加载中
const loading: Ref<boolean> = ref(false);
// 往外抛值
const onACSelect = (item: IData) => {
if (c.valueItem) {
emit('change', item[c.keyName], c.valueItem);
}
emit('change', item[c.textName]);
isShowAll.value = true;
};
// 值变更
const onSelect = (select: string | Array<string>) => {
const index = items.value.findIndex(item =>
Object.is(item[c.keyName], select),
);
if (index >= 0) {
onACSelect(items.value[index]);
}
};
// 搜索
const onSearch = async (query: string) => {
if (c.model.appDataEntity) {
// console.log('drop-down-query', query);
loading.value = true;
try {
const res = await c.getServiceData(query, props.data!);
loading.value = false;
if (res) {
items.value = res.data as IData[];
}
} catch (error) {
loading.value = false;
}
}
};
// 搜索词改变时触发
const queryChange = debounce((query: string) => {
if (
(query !== curValue.value || curValue.value === null) &&
allowSearch.value
) {
isShowAll.value = false;
onSearch(query);
}
}, 1000);
// 下拉打开
const onSelectOpen = (flag: boolean) => {
open.value = flag;
if (open.value) {
allowSearch.value = true;
items.value = [];
const query = isShowAll.value ? '' : curValue.value;
onSearch(query as string);
}
};
// 清除
const onClear = () => {
if (c.valueItem) {
emit('change', null, c.valueItem);
}
emit('change', null);
};
return {
ns,
c,
refValue,
loading,
items,
isShowAll,
allowSearch,
onSearch,
queryChange,
onSelectOpen,
onClear,
onSelect,
};
},
render() {
return (
<div class={this.ns.b()}>
<i-select
value={this.refValue}
filterable
allow-clear
clearable
loading={this.loading}
placeholder={this.c.placeHolder}
on-on-query-change={this.queryChange}
on-on-open-change={this.onSelectOpen}
on-on-change={this.onSelect}
on-on-clear={this.onClear}
>
{this.items.map((item, index) => {
return (
<i-option value={item[this.c.keyName]} key={index}>
{item[this.c.textName]}
</i-option>
);
})}
</i-select>
</div>
);
},
});
export default IBizPickerDropdown;
import { PickerEditorController } from '@ibiz-template/controller';
import { ref, watch, Ref, defineComponent, PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-picker/ibiz-picker.scss';
import { isEmpty } from 'lodash-es';
export const IBizPicker = defineComponent({
name: 'IBizPicker',
props: {
value: {
type: String,
},
controller: {
type: PickerEditorController,
},
data: {
type: Object as PropType<IData>,
required: true,
},
},
setup(props, { emit }) {
const ns = useNamespace('picker');
const c = props.controller!;
// 当前值
const curValue: Ref<Array<string> | string | null> = ref('');
// 实体数据集
const items: Ref<IData[]> = ref([]);
// 是否显示所有数据
const isShowAll = ref(true);
watch(
() => props.value,
newVal => {
if (newVal || newVal === null) {
curValue.value = newVal;
if (newVal === null) {
curValue.value = '';
}
const value = props.data[c.valueItem];
const index = items.value.findIndex((item: IData) =>
Object.is(item[c.keyName], value),
);
if (index !== -1) {
return;
}
// items里匹配不到当前值项值时,生成自身的选项
items.value = [];
if (!isEmpty(newVal) && !isEmpty(value)) {
items.value.push({ [c.textName]: newVal, [c.keyName]: value });
}
}
},
{ immediate: true },
);
// 处理视图关闭,往外抛值
const handleOpenViewClose = (result: IData[]) => {
const item: IData = {};
if (result && Array.isArray(result)) {
Object.assign(item, result[0]);
}
if (c.valueItem) {
emit('change', item[c.keyName], c.valueItem);
}
emit('change', item[c.textName]);
};
// 打开数据选择视图
const openPickUpView = async () => {
const res = await c.openPickUpView(props.data);
if (res) {
handleOpenViewClose(res);
}
};
// 打开数据链接视图
const openLinkView = async () => {
const res = await c.openLinkView(props.data);
if (res) {
handleOpenViewClose(res);
}
};
// 往外抛值
const onACSelect = (item: IData) => {
console.log('onACSelect');
if (c.valueItem) {
emit('change', item[c.keyName], c.valueItem);
}
emit('change', item[c.textName]);
isShowAll.value = true;
};
// 搜索
const onSearch = async (query: string) => {
console.log('picker-auto-query', query);
if (c.model.appDataEntity) {
const res = await c.getServiceData(query, props.data);
if (res) {
items.value = res.data as IData[];
}
}
};
// 清除
const onClear = () => {
if (c.valueItem) {
emit('change', null, c.valueItem);
}
emit('change', null);
};
// 聚焦
const onFocus = (e: IData) => {
const query = isShowAll.value ? '' : e.target.value;
onSearch(query);
};
return {
ns,
c,
curValue,
items,
openPickUpView,
openLinkView,
onACSelect,
onSearch,
onClear,
onFocus,
};
},
render() {
return (
<div class={this.ns.b()}>
{this.c.noAC ? (
<i-input
value={this.curValue}
clearable
placeholder={this.c.placeHolder}
on-on-clear={this.onClear}
>
{this.$slots.append}
{!this.$slots.append && this.c.pickupView ? (
<i-button
key='ios-search'
icon='ios-search'
on-click={this.openPickUpView}
></i-button>
) : null}
{!this.$slots.append && this.c.linkView ? (
<i-button
key='ios-search-outline'
icon='ios-search-outline'
on-click='openLinkView'
></i-button>
) : null}
</i-input>
) : (
<div class={this.ns.e('autocomplete')}>
<auto-complete
value={this.curValue}
placeholder={this.c.placeHolder}
placement='bottom'
clearable
transfer-class-name={this.ns.e('transfer')}
on-on-focus={this.onFocus}
on-on-search={this.onSearch}
on-on-clear={this.onClear}
>
{this.items.map(item => {
return (
<div
class={this.ns.e('transfer-item')}
on-click={() => {
this.onACSelect(item);
}}
>
{item[this.c.textName]}
</div>
);
})}
</auto-complete>
<div class={this.ns.e('btns')}>
{this.c.pickupView ? (
<i-button
icon='ios-search'
on-click={this.openPickUpView}
></i-button>
) : null}
{this.c.linkView ? (
<i-button
icon='ios-search-outline'
on-click={this.openLinkView}
></i-button>
) : null}
</div>
</div>
)}
</div>
);
},
});
export default IBizPicker;
import { RadioButtonListEditorController } from '@ibiz-template/controller';
import { computed, defineComponent, ref } from 'vue';
import type { PropType } from 'vue';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-radio-button-list/ibiz-radio-button-list.scss';
export default defineComponent({
props: {
value: [String, Number],
controller: {
type: RadioButtonListEditorController,
required: true,
},
data: {
type: Object as PropType<IData>,
required: true,
},
},
emits: {
change: (_value: string | number) => true,
},
setup(props, { emit }) {
const ns = useNamespace('radio-button-list');
const c = props.controller;
const editorModel = c.model;
const selectValue = computed({
get() {
return props.value || '';
},
set(newValue: string | number) {
emit('change', newValue);
},
});
const onSelectValueChange = (value: string | number) => {
selectValue.value = value;
};
// 代码表
const items = ref<readonly IData[]>([]);
props.controller.loadCodeList(props.data).then(codeList => {
items.value = codeList;
});
return {
ns,
editorModel,
selectValue,
items,
onSelectValueChange,
};
},
render() {
return (
<div class={this.ns.b()}>
<radio-group
class={this.ns.b('radio-group')}
value={this.selectValue}
on-on-change={this.onSelectValueChange}
>
{this.items.map((_item, index: number) => (
<radio
key={index}
label={_item.value}
readonly={this.editorModel.readOnly}
>
<span class={this.ns.be('radio-group', 'text')}>
{_item.text}
</span>
</radio>
))}
</radio-group>
</div>
);
},
});
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import { SpanEditorController } from '@ibiz-template/controller';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/editor/ibiz-span/ibiz-span.scss';
export default defineComponent({
props: {
value: String,
controller: {
type: SpanEditorController,
required: true,
},
data: {
type: Object as PropType<IData>,
required: true,
},
},
setup() {
const ns = useNamespace('span');
return {
ns,
};
},
render() {
return <span class={this.ns.b()}>{this.value}</span>;
},
});
import { EditorModel } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import { defineComponent } from 'vue';
import '@/styles/components/editor/not-supported-editor/not-supported-editor.scss';
export default defineComponent({
props: {
modelData: {
type: EditorModel,
required: true,
},
},
setup() {
const ns = useNamespace('not-supported-editor');
return { ns };
},
render() {
return (
<div class={this.ns.b()}>
未支持的编辑器类型 - {this.modelData.editorType}
</div>
);
},
});
import { defineComponent, ref } from 'vue';
import { IndexViewModel } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import { Icon, Layout } from 'view-design';
import '@/styles/components/layout/app-layout/app-layout.scss';
import { Namespace } from '@ibiz-template/core';
function renderLogo(ns: Namespace, model: IndexViewModel) {
return (
<div class={ns.e('logo')}>
{model.appIconPath ? (
<img class={ns.e('logo-img')} src={model.appIconPath}></img>
) : null}
<div class={ns.e('logo-caption')}>{model.caption}</div>
</div>
);
}
export const AppLayout = defineComponent({
name: 'AppLayout',
props: {
model: {
type: IndexViewModel,
required: true,
},
// 视图是否完成加载
isComplete: {
type: Boolean,
default: false,
},
},
setup(props, { emit }) {
const ns = useNamespace('layout');
// 菜单收缩变化
const collapseChange = ref(false);
const collapseMenus = () => {
collapseChange.value = !collapseChange.value;
emit('onCollapseChange', collapseChange.value);
};
const onBackClick = (event: MouseEvent) => {
event.stopPropagation();
emit('backClick');
};
return { ns, collapseChange, collapseMenus, onBackClick };
},
render() {
return this.isComplete ? (
<Layout
class={[this.ns.b(), this.ns.is('collapse', this.collapseChange)]}
>
<sider
hide-trigger
class={[this.ns.b('nav')]}
width={this.collapseChange ? 80 : 256}
value={this.collapseChange}
>
{renderLogo(this.ns, this.model)}
{this.$slots.menu}
</sider>
<layout class={[this.ns.b('content')]}>
<i-header class={this.ns.b('header')}>
<div class={this.ns.be('header', 'left')}>
{renderLogo(this.ns, this.model)}
<div class={this.ns.be('header', 'icon')}>
{!this.collapseChange ? (
<Icon
type='ios-arrow-back'
on-click={() => this.collapseMenus()}
/>
) : (
<Icon
type='ios-arrow-forward'
on-click={() => this.collapseMenus()}
/>
)}
</div>
<div
title='后退'
class={this.ns.be('header', 'back-icon')}
onClick={this.onBackClick}
>
<Icon type='ios-arrow-dropleft' />
</div>
{this.model.source.mainMenuAlign === 'TOP' ? (
<i-menu
class={this.ns.be('header', 'menu')}
mode='horizontal'
active-name='1'
></i-menu>
) : null}
</div>
<div class={this.ns.be('header', 'right')}>
<app-user />
</div>
</i-header>
<i-content class={this.ns.be('content', 'main')}>
{this.$slots.default}
</i-content>
{/* <i-footer class={this.ns.b('footer')}>Footer</i-footer> */}
</layout>
</Layout>
) : null;
},
});
import { defineComponent } from 'vue';
import { ControlModel } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/layout/control-layout/control-layout.scss';
/**
* 部件布局
*/
export default defineComponent({
props: {
modelData: ControlModel,
},
setup(props) {
const ns = useNamespace('control-layout');
const type = props.modelData?.source.controlType.toLowerCase();
return { ns, type };
},
render() {
return (
<div class={{ [this.ns.b()]: true, [this.ns.m(this.type)]: true }}>
{this.$slots.default}
</div>
);
},
});
import { AppLayout } from './app-layout/app-layout';
import ControlLayout from './control-layout/control-layout';
import { ViewBase } from './view-base/view-base';
import { ViewLayout } from './view-layout/view-layout';
export { AppLayout, ControlLayout, ViewLayout, ViewBase };
import { MDViewController, ViewController } from '@ibiz-template/controller';
import { useNamespace } from '@ibiz-template/vue-util';
import { defineComponent, PropType } from 'vue';
export const ViewBase = defineComponent({
props: {
controller: {
type: Object as PropType<ViewController>,
required: true,
},
},
setup() {
const ns = useNamespace('view');
return { ns };
},
render() {
const c = this.controller;
// 外面的插槽同样传给view-layout
const inheritSlots: IData = {};
Object.keys(this.$scopedSlots).forEach(key => {
if (key === 'default') {
inheritSlots[key] = (arg: IData) => [
c.viewLoading.isLoading && <spin size='large' fix></spin>,
this.$scopedSlots[key]!(arg),
];
} else {
inheritSlots[key] = (arg: IData) => this.$scopedSlots[key]!(arg);
}
});
return (
<view-layout
class={[
this.ns.b(),
c.complete && this.ns.b(this.controller.model.typeClass),
]}
isComplete={c.complete}
modelData={c.model}
viewMode={c.modal.mode}
scopedSlots={{
caption: () => {
if (c.complete && c.model.source.getPSSysImage()) {
return [
<app-icon icon={c.model.source.getPSSysImage()}></app-icon>,
c.caption,
];
}
return c.caption;
},
toolbar: () => {
if (c.complete && c.model.toolbar) {
return [
<view-toolbar
modelData={c.model.toolbar}
on-neuronInit={c.nerve.onNeuronInit(
c.model.toolbar.source.name,
)}
viewMode={c.modal.mode}
></view-toolbar>,
];
}
return null;
},
quickSearch: () => {
const _c = c as MDViewController;
if (_c.complete && _c.model.source.enableQuickSearch) {
return (
<quick-search
value={_c.query}
viewMode={c.modal.mode}
placeholder={_c.model.placeholder}
on-update={(val: string) => {
_c.query = val;
}}
on-search={() => _c.onSearch()}
></quick-search>
);
}
},
...inheritSlots,
}}
/>
);
},
});
import { computed, defineComponent } from 'vue';
import { ViewModel } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import '@/styles/components/layout/view-layout/view-layout.scss';
export const ViewLayout = defineComponent({
name: 'ViewLayout',
props: {
modelData: {
type: ViewModel,
required: true,
},
viewMode: {
type: String, // ViewMode 类型
default: 'ROUTE',
},
// 视图是否完成加载
isComplete: {
type: Boolean,
default: false,
},
isLoading: {
type: Boolean,
default: false,
},
},
setup(props) {
const ns = useNamespace('view-layout');
// 是否显示头部
const isShowHeader = computed(() => {
return props.modelData.source.showCaptionBar || !!props.modelData.toolbar;
});
return { ns, isShowHeader };
},
render() {
return !this.isComplete ? (
<div class={[this.ns.b(), this.ns.m(this.viewMode.toLowerCase())]}>
{this.isLoading ? <spin size='large' fix></spin> : null}
</div>
) : (
<div
class={[
this.ns.b(),
this.ns.m(this.modelData.modelClass),
this.ns.m(this.viewMode.toLowerCase()),
this.ns.is('no-header', !this.isShowHeader),
this.ns.is('loading', this.isLoading),
]}
>
{this.isLoading ? <spin size='large' fix></spin> : null}
{this.isShowHeader ? (
<div class={this.ns.b('header')}>
<div class={this.ns.b('header-content')}>
<div class={this.ns.be('header-content', 'left')}>
{this.modelData.source.showCaptionBar ? (
<div class={this.ns.be('header-content', 'caption')}>
{this.$scopedSlots.caption && this.$scopedSlots.caption({})}
</div>
) : null}
</div>
<div class={this.ns.be('header-content', 'right')}>
<div class={this.ns.e('quick-search')}>
{this.$scopedSlots.quickSearch &&
this.$scopedSlots.quickSearch({})}
</div>
<div class={this.ns.e('toolbar')}>
{this.$scopedSlots.toolbar && this.$scopedSlots.toolbar({})}
</div>
</div>
</div>
<div class={this.ns.b('header-exp')}></div>
</div>
) : null}
<div class={this.ns.b('top')}>
<div class={this.ns.be('top', 'message')}></div>
<div class={this.ns.be('top', 'search-form')}></div>
</div>
<div class={this.ns.b('content')}>
<div class={this.ns.be('content', 'left')}></div>
<div class={this.ns.be('content', 'body')}>
{this.$scopedSlots.default && this.$scopedSlots.default({})}
</div>
<div class={this.ns.be('content', 'right')}></div>
</div>
<div class={this.ns.b('footer')}>
{this.$scopedSlots.footer && this.$scopedSlots.footer({})}
</div>
</div>
);
},
});
import { defineComponent, getCurrentInstance, ref } from 'vue';
import { ModelUtil } from '@ibiz-template/model';
import {
IRouteViewData,
parseRouteViewData,
useRoute,
} from '@ibiz-template/vue-util';
import { Route } from 'vue-router';
import { getViewComponentName } from '@/util';
export default defineComponent({
name: 'RouterShell',
props: {
level: {
type: Number,
default: 0,
},
},
setup(props, ctx) {
const { proxy } = getCurrentInstance()!;
const route = useRoute(proxy) as Route;
const viewData = ref<IRouteViewData>({});
const isLoaded = ref(false);
const viewComponentName = ref('');
// 根据应用模型解析视图参数
ModelUtil.getModelService().then(service => {
const appModel = service.app;
if (appModel) {
// 获取视图
viewData.value = parseRouteViewData(appModel, route, props.level);
// 确定视图组件
viewComponentName.value = getViewComponentName(
viewData.value.viewType!,
);
isLoaded.value = true;
ctx.emit('viewFound', { modelPath: viewData.value.viewPath });
}
});
// 视图初始化事件,往上抛
const onNeuronInit = (...args: IData[]) => {
ctx.emit('neuronInit', ...args);
};
return {
route,
viewData,
isLoaded,
viewComponentName,
onNeuronInit,
};
},
render(h) {
if (!this.isLoaded) {
return null;
}
return h('AppTransition', {}, [
h(this.viewComponentName, {
props: {
context: this.viewData.context,
params: this.viewData.params,
modelPath: this.viewData.viewPath,
},
on: {
neuronInit: this.onNeuronInit,
},
}),
]);
},
});
import { IModal } from '@ibiz-template/runtime';
import { useEditViewController } from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType } from 'vue';
export const EditView = defineComponent({
props: {
context: Object as PropType<IContext>,
params: { type: Object as PropType<IParams> },
modelPath: { type: String, required: true },
modal: { type: Object as PropType<IModal> },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = useEditViewController(proxy, props.modelPath);
return { c };
},
render() {
return (
<view-base controller={this.c}>
{this.c.complete && (
<edit-form-control
modelData={this.c.model.form}
context={this.c.context}
params={this.c.params}
on-neuronInit={this.c.nerve.onNeuronInit(
this.c.model.form.source.name,
)}
></edit-form-control>
)}
</view-base>
);
},
});
import { IModal } from '@ibiz-template/runtime';
import { useGridViewController } from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType } from 'vue';
export const GridView = defineComponent({
props: {
context: Object as PropType<IContext>,
params: { type: Object as PropType<IParams>, default: () => ({}) },
modelPath: { type: String, required: true },
modal: { type: Object as PropType<IModal> },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = useGridViewController(proxy, props.modelPath);
return { c };
},
render() {
return (
<view-base controller={this.c}>
{this.c.complete && (
<grid-control
modelData={this.c.model.grid}
context={this.c.context}
params={this.c.params}
on-neuronInit={this.c.nerve.onNeuronInit(
this.c.model.grid.source.name,
)}
grid-row-active-mode={this.c.model.gridRowActiveMode}
></grid-control>
)}
</view-base>
);
},
});
import { GridView } from './grid-view/grid-view';
import { EditView } from './edit-view/edit-view';
import { PickupGridView } from './pickup-grid-view/pickup-grid-view';
import { PickupView } from './pickup-view/pickup-view';
import { MPickupView } from './mpickup-view/mpickup-view';
export * from './opt-view/opt-view';
export { GridView, EditView, PickupGridView, PickupView, MPickupView };
import {
useMPickupViewController,
useNamespace,
} from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType, Ref, ref } from 'vue';
import '@/styles/components/views/mpickup-view/mpickup-view.scss';
import { IModal } from '@ibiz-template/runtime';
export const MPickupView = defineComponent({
props: {
context: Object as PropType<IContext>,
params: { type: Object as PropType<IParams>, default: () => ({}) },
modelPath: { type: String, required: true },
modal: { type: Object as PropType<IModal> },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = useMPickupViewController(proxy, props.modelPath);
// model.typeClass
const ns = useNamespace('view-dempickupview');
// UI层单击选中的数组
const UISelections: Ref<IData[]> = ref([]);
const handleSelectionClick = (selection: IData) => {
const index = UISelections.value.indexOf(selection);
if (index === -1) {
UISelections.value.push(selection);
} else {
UISelections.value.splice(index, 1);
}
};
const isSelected = (selection: IData) => {
return UISelections.value.includes(selection);
};
const addRight = () => {
c.addSelections(c.embedSelection);
UISelections.value = [];
};
const addRightAll = () => {
c.selectAll();
UISelections.value = [];
};
const removeRight = () => {
c.removeSelections(UISelections.value);
UISelections.value = [];
};
const removeRightAll = () => {
c.selfSelection = [];
UISelections.value = [];
};
return {
ns,
c,
addRight,
addRightAll,
removeRight,
removeRightAll,
handleSelectionClick,
isSelected,
};
},
render() {
return (
<view-base
class={this.ns.b()}
controller={this.c}
scopedSlots={{
footer: () => {
return (
<div class={this.ns.b('footer')}>
<i-button
on-click={() => {
this.c.onOkButtonClick();
}}
>
确定
</i-button>
<i-button
on-click={() => {
this.c.onCancelButtonClick();
}}
>
取消
</i-button>
</div>
);
},
}}
>
<div class={this.ns.b('content')}>
{this.c.complete && this.c.model.pickupViewPanel && (
<div class={this.ns.b('left')}>
<pickup-view-panel
modelData={this.c.model.pickupViewPanel}
context={this.c.context}
params={this.c.params}
on-neuronInit={this.c.nerve.onNeuronInit(
this.c.model.pickupViewPanel.source.name,
)}
></pickup-view-panel>
</div>
)}
<div class={this.ns.b('center')}>
<i-button on-click={this.addRight} title='右移'>
{'>'}
</i-button>
<i-button on-click={this.removeRight} title='左移'>
{'<'}
</i-button>
<i-button on-click={this.addRightAll} title='全部右移'>
{'>>'}
</i-button>
<i-button on-click={this.removeRightAll} title='全部左移'>
{'<<'}
</i-button>
</div>
<div class={this.ns.b('right')}>
{this.c.selfSelection.map(item => {
return (
<div
key={item.srfkey}
class={[
this.ns.be('right', 'list-item'),
this.ns.is('selected', this.isSelected(item)),
]}
on-click={() => this.handleSelectionClick(item)}
>
{item.srfmajortext}
<icon
type='ios-close'
size={24}
on-click={() => {
this.c.removeSelections([item]);
}}
/>
</div>
);
})}
</div>
</div>
</view-base>
);
},
});
import { IModal } from '@ibiz-template/runtime';
import { useNamespace, useOptViewController } from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType } from 'vue';
import '@/styles/components/views/opt-view/opt-view.scss';
export const OptView = defineComponent({
props: {
context: Object as PropType<IContext>,
params: { type: Object as PropType<IParams> },
modelPath: { type: String, required: true },
modal: { type: Object as PropType<IModal> },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = useOptViewController(proxy, props.modelPath);
const ns = useNamespace('view-deoptview');
return { c, ns };
},
render() {
return (
<view-base
class={this.ns.b()}
controller={this.c}
scopedSlots={{
footer: () => {
return (
<div class={this.ns.b('footer')}>
<i-button
on-click={() => {
this.c.onOkButtonClick();
}}
>
确定
</i-button>
<i-button
on-click={() => {
this.c.onCancelButtonClick();
}}
>
取消
</i-button>
</div>
);
},
}}
>
{this.c.complete && (
<edit-form-control
modelData={this.c.model.form}
context={this.c.context}
params={this.c.params}
on-neuronInit={this.c.nerve.onNeuronInit(
this.c.model.form.source.name,
)}
></edit-form-control>
)}
</view-base>
);
},
});
import { IModal } from '@ibiz-template/runtime';
import { usePickupGridViewController } from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType } from 'vue';
export const PickupGridView = defineComponent({
props: {
context: Object as PropType<IContext>,
params: { type: Object as PropType<IParams>, default: () => ({}) },
modelPath: { type: String, required: true },
modal: { type: Object as PropType<IModal> },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = usePickupGridViewController(proxy, props.modelPath);
return { c };
},
render() {
return (
<view-base controller={this.c}>
{this.c.complete && (
<grid-control
modelData={this.c.model.grid}
context={this.c.context}
params={this.c.params}
on-neuronInit={this.c.nerve.onNeuronInit(
this.c.model.grid.source.name,
)}
grid-row-active-mode={this.c.model.gridRowActiveMode}
></grid-control>
)}
</view-base>
);
},
});
import { IModal } from '@ibiz-template/runtime';
import { usePickupViewController } from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType } from 'vue';
export const PickupView = defineComponent({
props: {
context: Object as PropType<IContext>,
params: { type: Object as PropType<IParams>, default: () => ({}) },
modelPath: { type: String, required: true },
modal: { type: Object as PropType<IModal> },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = usePickupViewController(proxy, props.modelPath);
return { c };
},
render() {
return (
<view-base controller={this.c}>
{this.c.complete && this.c.model.pickupViewPanel && (
<pickup-view-panel
modelData={this.c.model.pickupViewPanel}
context={this.c.context}
params={this.c.params}
on-neuronInit={this.c.nerve.onNeuronInit(
this.c.model.pickupViewPanel.source.name,
)}
></pickup-view-panel>
)}
</view-base>
);
},
});
此差异已折叠。
import { EditFormController } from '@ibiz-template/controller';
import { IBizContext } from '@ibiz-template/core';
import { EditFormModel } from '@ibiz-template/model';
import { useEditFormController } from '@ibiz-template/vue-util';
import { defineComponent, getCurrentInstance, PropType, reactive } from 'vue';
export const EditFormControl = defineComponent({
name: 'EditFormControl',
props: {
modelData: {
type: EditFormModel,
required: true,
},
context: {
type: IBizContext,
required: true,
},
params: { type: Object as PropType<IParams>, default: () => ({}) },
},
setup(props) {
const { proxy } = getCurrentInstance()!;
const c = useEditFormController(
proxy,
props.modelData,
props.context,
props.params,
);
// reactive后子组件才能监控和响应式更新
const reactiveC = reactive(c) as EditFormController;
return { c, reactiveC };
},
render() {
return !this.c.complete ? null : (
<form-control
model-data={this.modelData}
context={this.context}
controller={this.reactiveC}
></form-control>
);
},
});
export default EditFormControl;
import { FormButtonController } from '@ibiz-template/controller';
import { FormButtonModel } from '@ibiz-template/model';
import { useNamespace } from '@ibiz-template/vue-util';
import { defineComponent } from 'vue';
import '@/styles/components/widgets/form/form-button.scss';
export const FormButton = defineComponent({
name: 'FormButton',
props: {
modelData: {
type: FormButtonModel,
required: true,
},
controller: {
type: FormButtonController,
required: true,
},
},
setup() {
const ns = useNamespace('form-button');
return { ns };
},
render() {
if (!this.controller.visible) {
return null;
}
return (
<i-button class={this.ns.b()}>{this.modelData.source.caption}</i-button>
);
},
});
export default FormButton;
import { FormDRUIPartController, ViewNeuron } from '@ibiz-template/controller';
import { FormDRUIPartModel } from '@ibiz-template/model';
import { defineComponent, getCurrentInstance } from 'vue';
import { IModal, ViewMode } from '@ibiz-template/runtime';
import { useController, useNamespace } from '@ibiz-template/vue-util';
import { getViewComponentName } from '@/util';
import '@/styles/components/widgets/form/form-druipart.scss';
export const FormDRUIPart = defineComponent({
name: 'FormDRUIPart',
props: {
modelData: {
type: FormDRUIPartModel,
required: true,
},
controller: {
type: FormDRUIPartController,
required: true,
},
},
setup(props) {
const ns = useNamespace('form-druipart');
// 视图组件名称和视图模型路径
const viewComponentName = getViewComponentName(
props.modelData.embedView.source.viewType,
);
const viewPath = props.modelData.embedView.source.modelPath;
// 模态对象
const modal: IModal = { mode: ViewMode.EMBED };
// 绑定强制刷新
const { proxy } = getCurrentInstance()!;
useController(proxy, props.controller);
const onNeuronInit = (n: ViewNeuron) => {
props.controller.setViewNeuron(n);
};
return {
ns,
modal,
viewComponentName,
viewPath,
onNeuronInit,
};
},
render(h) {
if (!this.controller.visible || !this.controller.viewComponentKey) {
return null;
}
return (
<div
v-show={this.controller.visible}
layout-pos={this.modelData.source.getPSLayoutPos()}
class={this.ns.b()}
>
{h(this.viewComponentName, {
props: {
context: this.$props.controller.context,
params: this.$props.controller.params,
modal: this.modal,
modelPath: this.viewPath,
},
key: this.controller.viewComponentKey,
on: {
neuronInit: this.onNeuronInit,
},
})}
{this.controller.showMask ? (
<div class={this.ns.e('mask')}>请先保存主数据</div>
) : null}
</div>
);
},
});
export default FormDRUIPart;
import FormButton from './form-button/form-button';
import FormDRUIPart from './form-druipart/form-druipart';
import FormGroupPanel from './form-group-panel/form-group-panel';
import FormItemContainer from './form-item-container/form-item-container';
import FormItem from './form-item/form-item';
import FormPage from './form-page/form-page';
import FormPageItem from './form-page/form-page-item';
import FormRawItem from './form-raw-item/form-raw-item';
import FormTabPage from './form-tab-page/form-tab-page';
import FormTabPanel from './form-tab-panel/form-tab-panel';
import { FormControl } from './form-control';
export {
FormButton,
FormDRUIPart,
FormGroupPanel,
FormItemContainer,
FormItem,
FormPage,
FormPageItem,
FormRawItem,
FormTabPage,
FormTabPanel,
FormControl,
};
此差异已折叠。
此差异已折叠。
此差异已折叠。
export { AuthGuard } from './auth-guard/auth-guard';
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册