Commit 51d624c0 authored by 王纵's avatar 王纵

低代码渲染

parent dac50c4f
......@@ -5,5 +5,5 @@
}],
"stage-2"
],
"plugins": ["transform-runtime"]
"plugins": ["transform-runtime", "transform-vue-jsx"]
}
......@@ -4,6 +4,11 @@ const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const pkg = require('../package.json')
const myTheme = path.resolve(__dirname, "../src/global.less");
console.log('__dirname', __dirname, myTheme)
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
......@@ -66,7 +71,15 @@ exports.cssLoaders = function (options) {
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
less: generateLoaders('less', {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
modifyVars: {
// 直接覆盖变量
// 'tabs-default-color':'#dddddd',
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "${myTheme}";`
},
}),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
......
......@@ -9,6 +9,8 @@ function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const myTheme = path.resolve(__dirname, "./src/global.less");
let webpackConfig = {
context: path.resolve(__dirname, '../'),
entry: {
......@@ -40,6 +42,33 @@ let webpackConfig = {
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('packages')]
},
// {
// test: /\.less$/,
// use: [
// // ...其他 loader 配置
// {
// loader: 'less-loader',
// options: {
// // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
// lessOptions: {
// modifyVars: {
// // 直接覆盖变量
// 'text-color': '#111',
// 'border-color': '#eee',
// // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
// hack: `true; @import "your-less-file-path.less";`,
// },
// },
// },
// },
// ],
// },
{
test: /\.jsx?$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('packages')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
......
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:10:04
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-06 10:25:30
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
......@@ -51,7 +60,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
}),
]
})
console.log(JSON.stringify(devWebpackConfig))
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
......
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:10:04
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-07-29 17:19:16
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
......@@ -9,7 +18,8 @@ module.exports = merge(prodEnv, {
isMobilePlatform: false,
appCode:'"HLS_APP"',
clearTable: true,
VUE_APP_API_HOST:"'http://devapi.leafhzero.hand-china.com'",
VUE_APP_API_HOST:"'https://leafdevapi.hand-china.com'",
apiPath: '"https://leafdevapi.hand-china.com"',
loginPath: '"http://hlsapp.hand-china.com/core/oauth/token?client_id=hQGCtxTItRa34PUOgxaD0r7oSPeuEaIB&client_secret=7ee8338c-4a06-44a1-87cc-afa63f8e1bc3&grant_type=password&username=app&password=" ',
basePath: '"http://hlsapp.hand-china.com/core/r/api?sysName=HLS_APP&apiName="',
rootPath: '"http://hlsapp.hand-china.com/core/r/api"',
......
......@@ -56,6 +56,8 @@ export default {
caret-color: #3367d6;
}
.h-view {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow: hidden;
......
......@@ -176,6 +176,10 @@ export default {
height: 44,
}),
},
height: {
type: String,
default: ''
}
},
data () {
return {
......@@ -293,13 +297,18 @@ export default {
initScroll () {
let vm = this
if (!this.$refs.scroll) return
const headerHeight = vm.getHeaderHeight()
const nextHeight = vm.getNextElementHeight()
this.$refs.scroll.style.height = `${this.$refs.scroll.getBoundingClientRect().height - headerHeight - nextHeight}px`
if (this.height) {
this.$refs.scroll.style.height = this.height;
} else {
const headerHeight = vm.getHeaderHeight()
const nextHeight = vm.getNextElementHeight()
this.$refs.scroll.style.height = `${this.$refs.scroll.getBoundingClientRect().height - headerHeight - nextHeight}px`
}
// 设置scrollContent的最小高,实现高度不足时也有回弹效果
if (this.$refs.scrollContent) {
// const nextHeight = vm.getNextElementHeight()
this.$refs.scrollContent.style.minHeight = `${this.$refs.scroll.getBoundingClientRect().height}px`
this.$refs.scrollContent.style.minHeight = window.getComputedStyle(this.$refs.scroll).height; // `${this.$refs.scroll.getBoundingClientRect().height}px`
// this.$refs.scrollContent.style.paddingTop = `${headerHeight}px`
if (vm.hasFoot.footFlag) {
let height = vm.hasFoot.height || 88
......@@ -473,7 +482,8 @@ export default {
//$ = vue-better-scroll
.vue-better-scroll {
width 100%
overflow hidden !important
overflow auto
// overflow hidden !important
box-sizing border-box
position relative
//top 0
......
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-31 15:09:30
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-05 17:46:19
* @Version: 1.0.0
* @Description: 按钮渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="d-buttons">
<van-button
v-for="btn in defaultBtns"
round
type="info"
size="mini"
:key="btn.name"
class="d-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
<fragment v-if="buttons.length > num">
<span class="d-button-more" @click="more"><van-icon name="more-o" />更多</span>
<div class="d-button-more-content" :style="{display: moreVisible ? 'flex' : 'none'}">
<van-button
v-for="btn in hideBtns"
round
type="info"
size="mini"
:key="btn.name"
class="d-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
</div>
</fragment>
<fragment v-else>
</fragment>
</div>
</template>
<script>
import { Button, Icon } from 'vant';
export default {
name: 'DButtons',
components: {
[Button.name]: Button,
[Icon.name]: Icon,
},
props: {
tabInfo: { // 配置信息
type: Object,
default: () => {}
},
buttons: {
type: Array,
default: () => [],
},
},
data () {
return {
num: 2,
defaultBtns: [],
hideBtns: [],
moreVisible: false,
}
},
mounted() {
document.addEventListener('click', this.clickEle, false)
this.init();
},
destroyed () {
document.removeEventListener('click', this.clickEle, false)
},
methods: {
clickEle (e) {
let content = document.querySelector('.d-buttons')
if ((content && !content.contains(e.target))) {
this.moreVisible = false;
}
},
init() {
if(this.buttons.length > this.num) {
this.defaultBtns = this.buttons.slice(0, this.num);
this.hideBtns = this.buttons.slice(this.num, this.buttons.length);
} else {
this.defaultBtns = [...this.buttons];
}
},
more() {
this.moreVisible = true;
},
handlClick(btn) {
this.moreVisible = false;
console.log(btn.name)
}
},
}
</script>
<style scoped lang="less">
.d-buttons {
display: flex;
position: relative;
align-items: center;
justify-content: end;
.d-button {
padding: 0 12px;
height: 24px;
margin-left: 12px;
}
.d-button-more {
color: #666666;
font-size: 12px;
padding: 5px 8px;
margin-left: 12px;
}
.d-button-more-content {
z-index: 1;
position: absolute;
right: 0;
top: 100%;
display: flex;
flex-direction: column;
.d-button {
margin-top: 8px;
}
}
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:51:56
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 16:21:07
* @Version: 1.0.0
* @Description: 表单渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<fragment>
<DTitle v-if="showTitle" :title="tabInfo.description" :tabButtons="tabInfo.tabButtons || []" />
<van-form @submit="onSubmit">
<fragment v-for="field in fields" :key="field.columnName">
<!-- <h-field
v-if="fieldComponents.indexOf(field.validationTypeDisplay) > -1"
v-model="fieldsObj[field.columnName]"
:type="getInputType(field.validationTypeDisplay)"
placeholder="请输入"
:label="field.description"
:disabled="field.readOnly"
:required="field.required"
/> -->
<d-field
v-if="fieldComponents.indexOf(field.validationTypeDisplay) > -1"
v-model="fieldsObj[field.columnName]"
:fieldConfig="field"
:type="field.validationTypeDisplay"
placeholder="请输入"
:label="field.description"
:disabled="field.readOnly"
:required="field.required"
:clearable="field.clearFlag"
/>
<d-switch
v-if="field.validationTypeDisplay === 'Switch'"
:label="field.description"
v-model="fieldsObj[field.columnName]"
:disabled="field.readOnly"
:required="field.required"
@change="change1"
/>
<d-checkbox
v-if="field.validationTypeDisplay === 'CheckBox'"
:label="field.description"
v-model="fieldsObj[field.columnName]"
:disabled="field.readOnly"
:required="field.required"
@change="change1"
/>
<d-url
v-if="field.validationTypeDisplay === 'UrlField'"
:label="field.description"
v-model="fieldsObj[field.columnName]"
:disabled="field.readOnly"
:required="field.required"
@change="change1"
/>
<d-select
v-if="field.validationTypeDisplay === 'Select'"
v-model="fieldsObj[field.columnName]"
:label="field.description"
:fieldConfig="field"
:disabled="field.readOnly"
:required="field.required"
:clearable="field.clearFlag"
@change="change1"
/>
<d-lov
v-if="field.validationTypeDisplay === 'Lov'"
v-model="fieldsObj[field.columnName]"
:label="field.description"
:fieldConfig="field"
:disabled="field.readOnly"
:required="field.required"
:clearable="field.clearFlag"
@change="change1"
/>
<d-date
v-if="dateComponents.indexOf(field.validationTypeDisplay) > -1"
v-model="fieldsObj[field.columnName]"
:label="field.description"
:fieldConfig="field"
:disabled="field.readOnly"
:required="field.required"
:clearable="field.clearFlag"
/>
</fragment>
</van-form>
</fragment>
</template>
<script>
import { Field, Form, Button, Cell } from 'vant';
import DField from '../../FormItem/DField';
import DSwitch from '../../FormItem/DSwitch';
import DSelect from '../../FormItem/DSelect';
import DLov from '../../FormItem/DLov';
import DDate from '../../FormItem/DDate';
import DCheckbox from '../../FormItem/DCheckbox';
import DUrl from '../../FormItem/DUrl';
import DTitle from '../DTitle';
import DButtons from '../DButtons';
export default {
name: 'DForm',
components: {
[Form.name]: Form,
[Field.name]: Field,
[Button.name]: Button,
[Cell.name]: Cell,
DField,
DSwitch,
DSelect,
DLov,
DDate,
DCheckbox,
DUrl,
DTitle,
DButtons,
DCheckbox
},
props: {
record: {
type: Object,
default: () => ({}),
},
formType: {
type: String,
default: 'normal', // normal:正常表单, query: 查询表单
},
showTitle: {
type: Boolean,
default: true,
},
tabInfo: {
type: Object,
default: () => {}
},
fields: {
type: Array,
default: () => []
},
},
data () {
let initFields = {}
this.fields.forEach(item => {
initFields[item.columnName] = null;
})
return {
fieldComponents: ['TextField',"TextArea", "Currency", "NumberField", "EmailField","CentField"],
dateComponents: ["DatePicker", "DateTimePicker"],
componentTypes: ["UrlField"],
fieldsObj: {...initFields}
}
},
mounted() {
console.log(this.fields)
if (this.record && JSON.stringify(this.record) === '{}') {
this.fieldsObj = {...this.record};
}
},
methods: {
getInputType(type) {
console.log(type)
if (type === 'TextArea') {
return 'textarea';
}
if (['NumberField', 'Currency', 'CentField'].includes(type)) {
return 'number';
}
return 'text'
},
onSubmit() {
console.log('souusole', this.fieldsObj)
},
change1(val) {
console.log(val)
},
validate() {
console.log('字段娇艳', this.fieldsObj)
}
},
}
</script>
<style scoped lang="less">
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:51:56
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-08 22:34:26
* @Version: 1.0.0
* @Description: 表格渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<fragment>
<DTitle :title="tabInfo.description" :tabButtons="tabInfo.tabButtons || []" :buttonsFun="buttonsClick" />
<scroll
ref="scroll" class="has-footer" :pullUp="true" :pull-down="true" @pullingUp="getList"
@pullingDown="getList"
height="calc(100vh - 130px)"
>
<van-checkbox-group v-model="checkResult">
<div v-for="(item, index) in tableData" :key="item[tabInfo.primaryKey]" class="d-table" @click="showRecord(item, index)">
<fragment v-for="(column, index) in columns" :key="column.columnName" >
<div v-if="index === 0" class="d-table-th">
<van-checkbox :name="item[tabInfo.primaryKey]">{{column.description}}: {{item[column.columnName]}}</van-checkbox>
</div>
<div v-else class="d-table-tr" @click="toDetail">
<span>{{column.description}}</span>
<span>{{(column.validationTypeDisplay === 'Switch' ? (item[column.columnName] ? '是' : '否' ) : item[column.columnName]) || '--'}}</span>
</div>
</fragment>
</div>
</van-checkbox-group>
<van-popup v-model="visible" position="top" class="d-table-form-popup" get-container="body">
<div class="form-content">
<d-form :fields="columns" ref="dformRef" :record="record" :showTitle="false" />
</div>
<div class="d-table-form-footer">
<van-button
round
@click.native="cancelRecord"
>取消</van-button>
<van-button
type="info"
round
@click.native="saveRecord"
>保存</van-button>
</div>
</van-popup>
</scroll>
</fragment>
</template>
<script>
import DTitle from '../DTitle';
import DForm from '../DForm';
import { Checkbox, CheckboxGroup, Popup, Button } from 'vant';
import {get} from '../../utils/hlsHttp';
export default {
name: 'DTable',
components: {
DTitle,
DForm,
[Button.name]: Button,
[Checkbox.name]: Checkbox,
[CheckboxGroup.name]: CheckboxGroup,
[Popup.name]: Popup,
},
props: {
tabInfo: { // 配置信息
type: Object,
default: () => {}
},
buttons: {
type: Array,
default: () => [],
},
columns: {
type: Array,
default: () => [],
}
},
data () {
return {
page: 0,
size: 10,
tableData: [], // 表格数据
checkResult: [],
visible: false,
record: {}, // 操作记录
recordIndex: -1,
buttonsFun: {}
}
},
mounted() {
this.init();
},
methods: {
init() {
this.getList();
console.log('columns', this.columns)
},
getList() {
const params = {
page: this.page,
size: this.size,
}
get(this.tabInfo.readUrl, params).then(res => {
this.tableData = this.tableData.concat(res.content || []);
if (res.totalElements > this.tableData.length) {
this.page = this.page + 1;
this.$refs.scroll.update(false)
} else {
this.$refs.scroll.update(true)
}
})
},
showRecord(res, index) {
this.record = {...res};
this.recordIndex = index;
this.visible = true;
},
saveRecord() {
this.$refs.dformRef.validate();
this.visible = false;
},
cancelRecord() {
this.visible = false;
},
toDetail(e) {
e.stopPropagation();
e.preventDefault()
this.$router.push({
name: 'DynamicDetail',
// params: params
})
},
// 删除
deleteRecords() {
},
buttonsClick(btn) {
console.log(btn)
switch (btn.name) {
case 'create':
this.showRecord({}, -1);
break;
case 'query':
console.log('查询')
break;
case 'export':
console.log('导出')
break;
case 'remove':
this.deleteRecords();
break;
case 'save':
console.log('保存')
break;
default:
}
}
},
}
</script>
<style scoped lang="less">
.d-table {
margin: 0 8px 12px 8px;
padding: 4px 8px 12px 8px;
background-color: #fff;
border-radius: 4px;
.d-table-tr {
display: flex;
justify-content: space-between;
padding: 10px 16px 0 16px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #646465;
}
.d-table-th {
padding: 8px 0 12px 8px;
border-bottom: 1px solid #F3F3F7;;
font-family: PingFangSC-Semibold;
font-size: 15px;
color: #4B4A4B;
font-weight: 600;
}
}
.d-table-form-popup {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.form-content {
flex: 1;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
}
.d-table-form-footer {
display: flex;
justify-content: space-between;
padding: 10px 16px;
background: #fafafa;
box-shadow: 0 -.533vw 2.4vw 0 #e2e2e2;
.van-button {
width: 48%;
font-size: 16px;
font-weight: 600;
}
}
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-02 15:32:36
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-08 22:40:26
* @Version: 1.0.0
* @Description: 组件头部渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="layout-title">
<div class="title">{{title}}</div>
<div class="title-btns">
<van-button
v-for="btn in defaultBtns"
round
size="mini"
:key="btn.name"
:icon="buttonIcon[btn.name]"
class="title-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
<van-popover
v-if="tabButtons.length > num"
v-model="moreVisible"
trigger="click"
placement="bottom-end"
:actions="actions"
@select="handlClick"
:offset="[12, 14]"
>
<template #reference>
<span class="title-button-more">更多</span>
</template>
</van-popover>
<!-- <fragment v-if="tabButtons.length > num"> -->
<!-- <van-popover
v-model="moreVisible"
trigger="click"
:actions="actions"
@select="handlClick"
>
<template #reference>
<span class="title-button-more" @click="more">更多</span>
</template>
</van-popover> -->
<!-- <span class="title-button-more" @click="more">更多</span>
<div class="title-button-more-content" :style="{display: moreVisible ? 'flex' : 'none'}">
<van-button
v-for="btn in hideBtns"
round
size="mini"
:key="btn.name"
:icon="buttonIcon[btn.name]"
class="title-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
</div> -->
<!-- </fragment> -->
</div>
<!-- <div class="flex-item title-btns"><d-buttons :buttons="tabButtons" /></div> -->
</div>
</template>
<script>
import { Button, Icon, Popover } from 'vant';
import DButtons from '../DButtons';
export default {
name: 'DTitle',
components: {
[Icon.name]: Icon,
[Button.name]: Button,
[Popover.name]: Popover,
DButtons
},
props: {
title: {
type: String,
default: '',
},
tabButtons: {
type: Array,
default: () => []
},
buttonsFun: {
type: Function,
default: () => {}
}
},
data () {
return {
num: 2,
defaultBtns: [],
hideBtns: [],
actions: [],
moreVisible: false,
buttonIcon: {
create: 'add-o',
save: 'description-o',
export: '',
remove: 'delete-o',
query: 'search'
}
}
},
mounted() {
this.init();
},
methods: {
init() {
if(this.tabButtons.length > this.num) {
this.defaultBtns = this.tabButtons.slice(0, this.num);
this.hideBtns = this.tabButtons.slice(this.num, this.tabButtons.length);
this.actions = this.hideBtns.map(o => ({...o, text: o.description}));
console.log('this.actions', this.actions)
} else {
this.defaultBtns = [...this.tabButtons];
}
},
handlClick(btn) {
this.buttonsFun(btn);
console.log(btn.name)
}
},
}
</script>
<style scoped lang="less">
@import '../../index.less';
.layout-title{
display: flex;
flex-wrap: nowrap;
align-items: center;
padding: 15px 14px;
.title {
position: relative;
max-width: 50%;
font-family: PingFangSC-Medium;
font-size: 14px;
color: #333333;
font-weight: 500;
// border-left: 5px solid @primary-color;
&::before {
content: '';
background-color: @primary-color;
position: absolute;
left: -14px;
top: 50%;
margin-top: -9px;
width: 4px;
height: 18px;
border-radius: 0px 20px 20px 0px;
}
}
.title-btns{
// text-align: right;
flex: 1;
display: flex;
position: relative;
align-items: center;
justify-content: end;
.title-button {
padding: 0 12px;
height: 24px;
margin-left: 12px;
font-family: PingFangSC-Medium;
font-size: 12px;
color: rgba(19,19,19,0.80);
line-height: 18px;
font-weight: 500;
background-color: #f7f7f7;
border: 1px solid #DEDEDE;
}
.van-popover__wrapper {
margin-left: 12px;
}
.title-button-more {
font-family: PingFangSC-Medium;
font-size: 12px;
color: rgba(19,19,19,0.60);
}
// .title-button-more-content {
// z-index: 1;
// position: absolute;
// right: 0;
// top: 100%;
// display: flex;
// flex-direction: column;
// .title-button {
// margin-top: 8px;
// }
// }
}
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:51:56
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 16:22:15
* @Version: 1.0.0
* @Description: 查询表单渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div>
<van-search
v-model="searchValue"
shape="round"
show-action
placeholder="请输入搜索关键词"
@search="onSearch"
>
<template #action>
<van-icon name="filter-o" @click="showForm" />
</template>
</van-search>
<van-popup v-model="show" position="top">
<DForm :fields="fields" ref="dformRef" formType="query" :showTitle="false" />
<div style="margin: 16px;">
<h-button
type="primary"
v-for="btn in (tabInfo.tabButtons || [])"
:key="btn.name"
style="margin-left: 10px"
@click.native="btnFun(btn)"
>{{btn.description}}</h-button>
</div>
</van-popup>
</div>
</template>
<script>
import { Search, Icon, Popup } from 'vant';
import QueryFormFields from './query-form-fields';
import DForm from '../DForm';
export default {
name: 'QueryForm',
components: {
QueryFormFields,
DForm,
[Search.name]: Search,
[Icon.name]: Icon,
[Popup.name]: Popup
},
props: {
fields: {
type: Array,
default: () => []
},
tabInfo: {
type: Object,
default: () => {}
}
},
data () {
return {
show: false,
searchValue: '',
}
},
inject: ['config', 'testname'],
mounted() {
this.configDatas = this.config();
console.log('config', this.config())
},
methods: {
onSearch() { // 搜索
console.log('souusole')
},
showForm() {
this.show = true;
},
btnFun(btn) {
console.log('过来了吗', this.$refs.dformRef)
this.$refs.dformRef.validate();
}
},
}
</script>
<style scoped lang="less">
.content{
//background-color: #cfcfcf;
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:51:56
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-05 17:45:52
* @Version: 1.0.0
* @Description: 查询表单渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<van-form @submit="onSubmit">
<fragment v-for="field in fields" :key="field.columnName">
<h-field
v-if="fieldComponents.includes(field.validationTypeDisplay)"
v-model="fieldsObj[field.columnName]"
:type="getInputType(field.validationTypeDisplay)"
placeholder="请输入"
:label="field.description"
:disabled="field.readOnly"
:required="field.required"
/>
<d-switch
v-if="field.validationTypeDisplay === 'Switch'"
:label="field.description"
v-model="fieldsObj[field.columnName]"
:disabled="field.readOnly"
:required="field.required"
@change="change1"
/>
<d-select
v-if="field.validationTypeDisplay === 'Select'"
v-model="fieldsObj[field.columnName]"
:label="field.description"
:fieldConfig="field"
:disabled="field.readOnly"
:required="field.required"
@change="change1"
/>
<d-lov
v-if="field.validationTypeDisplay === 'Lov'"
v-model="fieldsObj[field.columnName]"
:label="field.description"
:fieldConfig="field"
:disabled="field.readOnly"
:required="field.required"
@change="change1"
/>
</fragment>
<div style="margin: 16px;">
<!-- <van-button round type="info" native-type="submit" v-for="">重置</van-button>
<van-button round type="info" native-type="submit">提交</van-button> -->
<h-button type="primary" v-for="btn in buttons" :key="btn.name" style="margin-left: 10px">{{btn.description}}</h-button>
</div>
</van-form>
</template>
<script>
import { Field, Form, Button, Cell, Switch } from 'vant';
import DSwitch from '../../FormItem/DSwitch';
import DSelect from '../../FormItem/DSelect';
import DLov from '../../FormItem/DLov';
export default {
name: 'QueryFormFields',
components: {
[Form.name]: Form,
[Field.name]: Field,
[Button.name]: Button,
[Cell.name]: Cell,
DSwitch,
DSelect,
DLov
},
props: {
fields: {
type: Array,
default: () => []
},
buttons: {
type: Array,
default: () => []
}
},
data () {
let initFields = {}
this.fields.forEach(item => {
initFields[item.columnName] = null;
})
return {
fieldComponents: ['TextField',"TextArea", "Currency", "NumberField", "EmailField","CentField"],
componentTypes: ['TextField', "TextArea", "Currency", "NumberField", "EmailField", "Select", "DatePicker",
"DateTimePicker", "CheckBox", "Switch", "Lov", "UrlField", "CentField"],
fieldsObj: {...initFields}
}
},
methods: {
getInputType(type) {
if (type === 'TextArea') {
return 'textarea';
}
if (['NumberField', 'Currency', 'CentField'].includes(type)) {
return 'number';
}
return 'text'
},
onSubmit() {
console.log('souusole', this.fieldsObj)
},
change1(val) {
console.log(val)
}
},
}
</script>
<style scoped lang="less">
</style>
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-30 09:41:54
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 15:55:35
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
import Vue from 'vue';
import {Button} from 'vant'
import layouts from '../layout';
import {ColumnBuilder, TabButtonBuilder, ComponentBuilder} from './utils.jsx'
import QueryForm from './QueryForm';
import DTable from './DTable';
import DForm from './DForm';
const ConfigRenderComponent = {
// functional: true,
// Props 是可选的
components: {
QueryForm,
DTable,
DForm,
},
props: {
needBack: { // 是否需要返回键
type: Boolean,
default: true
},
staticLayoutTabs: {
type: Array,
default: () => [],
}
},
data () {
return {
configInfo: {}, // 配置信息
layoutComponents: ['HBox', 'VBox', 'Tabs', 'TabPane', 'NavigationBar'],
queryParams: {}, // 路由参数
}
},
inject: ['config'],
created() {
this.queryParams = {
...this.$route.params,
...this.$route.query
}
},
mounted() {
this.configInfo = this.config();
},
methods: {
getTabs(layoutTabs, state, layoutCode, createElement) {
return layoutTabs.map(layoutTab => {
const parentTabCode = layoutTab.tabCode;
// 布局类型
const layoutName = layoutTab.tabType;
// 静态嵌入布局
if (layoutName === 'Layout' && layoutTab.tabStatusType === 'STATIC') {
const currentStaticLayoutTabs = this.staticLayoutTabs.filter(
value => value.tabCode === parentTabCode
);
if (currentStaticLayoutTabs.length > 0 && currentStaticLayoutTabs[0].component) {
const StaticComponent = currentStaticLayoutTabs[0].component;
return [<StaticComponent {...this} />];
}
return [];
}
if (!layoutTab.childrenTabs || layoutTab.childrenTabs.length === 0) {
return [];
}
// 组件描述
const componentNames = [];
let components;
if (this.layoutComponents.some(item => layoutTab.childrenTabs[0].tabType === item)) {
layoutTab.childrenTabs.forEach(childrenTab => {
// componentNames.push(childrenTab.description);
componentNames.push({code: childrenTab.tabCode, label: childrenTab.description})
});
components = this.getTabs(layoutTab.childrenTabs, state, layoutCode, createElement);
} else {
components = layoutTab.childrenTabs.map(childrenTab => {
const {
tabType,
tabCode,
baseTable,
parentTable,
tabFields,
tabButtons,
description
} = childrenTab;
// componentNames.push(description);
componentNames.push({code: tabCode, label: description});
const columns = new ColumnBuilder()
.withFields(tabFields)
.withType(tabType)
.withTabCode(tabCode)
// .withTableFooterRenderers(tableFooterRenderers)
.withLayoutCode(childrenTab.layoutCode || layoutCode)
.withParentTabCode(parentTabCode)
// .withComponentRenderers(componentRenderers)
.build();
// console.log('===init', columns, tabType)
// const buttons = new TabButtonBuilder()
// .withCreateElement(createElement)
// .withType(tabType)
// .withTabButtons(tabButtons)
// .withTabCode(tabCode)
// .withLayoutCode(childrenTab.layoutCode || layoutCode)
// // .withComponentButtons(componentButtons)
// // .withReadOnly(param[Constants.FIELD_READONLY])
// .build();
// return <div>{tabCode}-{description} {buttons}</div>
const findField = find(tabFields, { formatCascadeFlag: 'Y' });
return new ComponentBuilder()
// .withDataSet(dataSet)
// .withHistoryDataSet(dataSets[`history_${dataSetName}`])
// .withDifference(differenceMap[dataSetName])
.withCascadeKey(findField && findField.columnName)
// .withHistoryButtonName(historyButtonName)
// .withCurrentButtonName(currentButtonName)
.withRouteParams(this.queryParams)
.withCreateElement(createElement)
.withTabType(tabType)
.withColumns(columns)
// .withButtons(buttons)
.withLayoutCode(childrenTab.layoutCode || layoutCode)
.withTabCode(childrenTab.tabCode)
.withProps(childrenTab)
// .withComponentHeaderExtras(componentHeaderExtras)
.build();
});
}
const Layout = this.getLayout(layoutName);
return <Layout componentNames={componentNames} components={components}/>
// return new LayoutBuilder()
// .withLayout(this.getLayout(layoutName))
// .withTabCode(layoutTab.tabCode)
// // .withTabMap(tabMap)
// .withComponents(components)
// .withComponentNames(componentNames)
// // .withComponentFunctions(componentFunctions)
// .build();
});
},
getLayout(layoutName) {
return layouts[layoutName];
}
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (h, context) {
console.log('context', context, this.configInfo)
const { layoutCode, layoutTabs = [] } = this.configInfo;
return <div style="height: 100%">{layoutTabs.length ? this.getTabs(layoutTabs, this, layoutCode, h) : null}</div>;
}
};
export default ConfigRenderComponent;
\ No newline at end of file
This diff is collapsed.
/*
* @Description: In User Settings Edit
* @Author: huangtianyang
* @Date: 2020-01-16 09:41
*/
// import {
// CentField,
// CheckBox,
// Currency,
// DatePicker,
// DateTimePicker,
// EmailField,
// Lov,
// MonthPicker,
// NumberField,
// Radio,
// Select,
// Switch,
// TextArea,
// TextField,
// TimePicker
// } from 'leaf-ui/pro';
import {
Field,
Checkbox,
Radio,
Switch,
} from 'vant';
import { isNil } from 'lodash';
import Vue from 'vue'
// 表单字段
const FormFields = {
"TextField": Field,
Checkbox,
Radio,
Switch,
};
export default function FormField(field) {
const {
columnName: name,
columnCount: colSpan,
rowCount: rowSpan,
description: label,
clearButton,
dataSet,
columnSeq
} = field;
const fieldProps = {
...field,
name,
colSpan,
rowSpan,
label,
clearButton: clearButton === 'Y',
newLine: columnSeq === 1,
disabled: !isNil(dataSet) && dataSet.fields.get(name).props.readOnly,
id: undefined
};
const FieldComponent = FormFields[field.validationTypeDisplay];
console.log('FieldComponent', FieldComponent)
if (field.dataType === 'multiple') {
fieldProps.multiple = true;
}
/* if (field.validationTypeDisplay === 'CentField') {
fieldProps.renderer = ({ text, value }) => {
if (isNil(value)) {
return text;
} else {
return NumberField.format(value, undefined, { minimumFractionDigits: 4 });
}
};
} else */
if (field.validationTypeDisplay === 'TextArea') {
fieldProps.resize = 'both';
} else if (field.validationTypeDisplay === 'Currency' && field.isHundredth === 1) {
fieldProps.renderer = ({ text, value }) => {
if (isNil(value)) {
return text;
}
const fixed = value.toFixed(2);
const splits = fixed.split('.');
return `${NumberField.format(splits[0])}.${splits[1]}`;
};
}
return FieldComponent // <FieldComponent {...fieldProps} disabledIfReadOnly />;
}
/*
* @Description: In User Settings Edit
* @Author: gaoyang
* @Date: 2020-04-08 15:23
* @Purpose:
*/
export const omitFormConfigProps = [
'creationDate',
'createdBy',
'lastUpdateDate',
'lastUpdatedBy',
'objectVersionNumber',
'_token',
'layoutId',
'parentId',
'tabCode',
'tabType',
'seq',
'rowCount',
'columnCount',
'primaryKey',
'tabButtons',
'tabFields',
'layoutCode',
'buttons'
];
/*
* @Description: In User Settings Edit
* @Author: huangtianyang
* @Date: 2020-04-02 12:10
*/
import { Form } from 'leaf-ui/pro';
import { omit } from 'lodash';
import { omitFormConfigProps } from './formConfigConstant';
import React, { Component } from 'react';
import classNames from 'classnames';
import intl from 'utils/intl';
import png from './img/down.png';
import style from './index.less';
class HlsForm extends Component {
constructor(props) {
super(props);
this.state = {
isButtonShown: this.props.buttons && this.props.buttons.length > 0,
hidden: process.env.REACT_APP_FORM_FOLD === 'true'
};
}
render() {
const queryFieldsLimit = this.props.columns;
const handleToggle = () => {
const { hidden } = this.state;
this.setState({
hidden: !hidden
});
};
return (
<div className={style['hls-from']}>
<div className={style.form}>
<Form
ref={ref => {
this.props && this.props.setRef(ref);
}}
{...omit(this.props, omitFormConfigProps)}
onKeyDown={e => {
if (e.keyCode === 13 && this.props.buttons) {
this.props.buttons.some(button => {
if (button.key === 'query') {
button.props.onClick();
return true;
}
return false;
});
}
}}
>
{!this.state.hidden && this.state.isButtonShown
? this.props.children && this.props.children.slice(0, queryFieldsLimit)
: this.props.children}
</Form>
</div>
<div
className={style.buttons}
style={{ display: this.state.isButtonShown ? 'block' : 'none' }}
>
{this.props.buttons}
{process.env.REACT_APP_FORM_FOLD === 'true' ? null : (
<span
onClick={handleToggle}
className={style.more}
style={{
display:
this.props.children && this.props.children.length > queryFieldsLimit
? 'inline-block'
: 'none'
}}
>
{!this.state.hidden
? intl.get('hzero.common.button.expand').d('展开')
: intl.get('hzero.common.button.up').d('收起')}
<img
src={png}
className={classNames(style.icon, this.state.hidden ? style.animate : '')}
alt={
!this.state.hidden
? intl.get('hzero.common.button.expand').d('展开')
: intl.get('hzero.common.button.up').d('收起')
}
/>
</span>
)}
</div>
</div>
);
}
}
export default HlsForm;
.hls-from {
display: flex;
margin-bottom: 5px;
.form {
flex-grow: 1;
width: 0;
}
.buttons {
// width: 200px;
margin: 5px 0 0 0;
}
}
.more {
color: #3f85ff;
display: inline-block;
height: 28px;
padding: 0 0 0 0.18rem;
line-height: 28px;
text-align: center;
vertical-align: middle;
background-color: transparent;
border: none;
border-radius: 2px;
outline: none;
-webkit-box-shadow: none;
box-shadow: none;
cursor: pointer;
.icon {
width: 16px;
height: 16px;
transition: transform 0.2s;
}
.animate {
transform: rotate(-180deg);
-ms-transform: rotate(-180deg); //IE 9
-webkit-transform: rotate(-180deg); // Safari and Chrome
-o-transform: rotate(-180deg); // Opera
-moz-transform: rotate(-180deg); // Firefox
}
}
/*
* @Description: 所有页面组件
* @Author: huangtianyang
* @Date: 2020-01-08 18:42
*/
// import { Table } from 'leaf-ui/pro';
// import Form from './HlsForm';
import FormField from './FormField';
import {Form} from 'vant'
export default { Form, FormField };
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 10:58:40
* @Version: 1.0.0
* @Description: 动态渲染-复选框组件
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<van-cell center class="d-form-item" :required="required" :title="label">
<!-- <template #title>
<span v-if="required" class="required">*</span>
{{label}}
</template> -->
<template #right-icon>
<van-checkbox v-model="currentValue" @change="checked" :disabled="disabled" />
</template>
</van-cell>
</template>
<script>
import {Cell, Checkbox} from 'vant';
export default {
name: 'DCheckbox',
components: {
[Cell.name]: Cell,
[Checkbox.name]: Checkbox,
},
props: {
label: {
type: String,
default: ''
},
value: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
}
},
data () {
return {
currentValue: false
}
},
watch: {
value: function(newValue, oldValue) {
if (newValue !== oldValue) {
this.currentValue = newValue
}
}
},
methods: {
checked (flag) {
if (!this.disable) {
this.$emit('input', flag)
this.$emit('change', flag)
}
},
},
}
</script>
<style lang="less">
@import './index.less';
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 15:56:19
* @Version: 1.0.0
* @Description: 动态渲染-日期组件
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="d-form-item">
<van-cell center :required="required">
<d-label :label="label" :help="fieldConfig.help" slot="title" />
<span @click="showDate(true)" class="cell-value">
<input placeholder="请选择" :value="currentValue" readonly class="value-input"/>
<van-icon name="clear" v-show="clearable && currentValue" @click="clear"/>
<img src="../assets/date-icon.png" alt="" class="right-icon" v-show="!(clearable && currentValue)"/>
</span>
</van-cell>
<van-popup v-model="visible" round position="bottom" class="d-date-popup">
<van-datetime-picker
v-model="pickerValue"
:type="dateType[type]"
:title="`选择${fieldConfig.description}`"
@confirm="dateConfirm"
@cancel="showDate(false)"
/>
</van-popup>
</div>
</template>
<script>
import {Cell, DatetimePicker, Popup, Icon} from 'vant';
import DLabel from './DLabel';
export default {
name: 'DDate',
components: {
[Cell.name]: Cell,
[DatetimePicker.name]: DatetimePicker,
[Popup.name]: Popup,
[Icon.name]: Icon,
DLabel
},
props: {
fieldConfig: {
type: Object,
default: () => {}
},
type: {
type: String,
default: 'DatePicker'
},
label: {
type: String,
default: ''
},
value: {
type: String,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
},
clearable: {
type: Boolean,
default: false,
}
},
data () {
return {
dateType: {
DatePicker: 'date',
DateTimePicker: 'datetime'
},
currentValue: '',
pickerValue: '',
visible: false,
}
},
watch: {
value: function(newValue, oldValue) {
if (newValue !== oldValue) {
this.currentValue = newValue
}
}
},
methods: {
clear(e) {
e.preventDefault()
e.stopPropagation()
this.dateConfirm('')
},
showDate(flag) {
if (!this.disabled) {
this.visible = flag;
if (flag) {
this.pickerValue = this.currentValue ? new Date(this.currentValue) : '';
}
}
},
dateConfirm(date) {
this.showDate(false);
this.currentValue = date ? this.dateFormat(date) : '';
this.$emit('input', this.currentValue);
this.$emit('change', this.currentValue);
},
dateFormat(time) { // 时间格式化 2019-09-08
let year = time.getFullYear();
let month = time.getMonth() + 1;
let day = time.getDate();
return `${year}-${month < 10 ? `0`: ''}${month}-${day < 10 ? `0`: ''}${day}`;
}
},
}
</script>
<style lang="less">
@import './index.less';
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 14:21:09
* @Version: 1.0.0
* @Description: 动态渲染-文本框组件
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="d-form-item">
<van-field
v-model="currentValue"
placeholder="请输入"
input-align="right"
@input="fieldInput"
:type="fieldType[type]"
:disabled="disabled"
:required="required"
:clearable="clearable"
:formatter="formatter"
>
<d-label :label="label" :help="fieldConfig.help" slot="label" />
<span slot="extra" v-if="type === 'CentField'">%</span>
</van-field>
</div>
</template>
<script>
import {Cell, Field, Icon} from 'vant';
import DLabel from './DLabel';
export default {
name: 'DCheckbox',
components: {
[Cell.name]: Cell,
[Field.name]: Field,
[Icon.name]: Icon,
DLabel
},
props: {
fieldConfig: {
type: Object,
default: {}
},
type: {
type: String,
default: 'TextField',
},
label: {
type: String,
default: ''
},
value: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
},
clearable: {
type: Boolean,
default: false,
}
},
data () {
return {
fieldType: {
TextField: 'text',
TextArea: 'textarea',
NumberField: 'number',
EmailField: 'text',
CentField: 'number',
Currency: 'number'
},
currentValue: '',
}
},
watch: {
value: function(newValue, oldValue) {
if (newValue !== oldValue) {
this.currentValue = newValue
}
}
},
methods: {
formatter(val) {
// if (this.type === 'Currency') {
// return `${val}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
// }
return val;
},
fieldInput (val) {
console.log('val', val)
this.$emit('input', val)
this.$emit('change', val)
},
},
}
</script>
<style lang="less">
@import './index.less';
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 11:38:16
* @Version: 1.0.0
* @Description: 动态渲染-label组件
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<span class="d-label">
{{label}}
<van-popover
v-model="showPopover"
trigger="click"
theme="dark"
placement="top"
v-if="help"
>
<div class="d-label-popver">{{help}}</div>
<template #reference>
<van-icon name="info-o" color="#1989fa" />
</template>
</van-popover>
</span>
</template>
<script>
import {Cell, Field, Icon, Popover} from 'vant';
export default {
name: 'DLabel',
components: {
[Cell.name]: Cell,
[Field.name]: Field,
[Icon.name]: Icon,
[Popover.name]: Popover,
},
props: {
label: {
type: String,
default: ''
},
help: {
type: String,
default: '',
},
},
data () {
return {
showPopover: false,
}
},
}
</script>
<style lang="less">
.d-label-popver {
padding: 4px 8px;
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 22:35:19
* @Version: 1.0.0
* @Description: 动态渲染-Lov
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="d-form-item d-lov">
<van-cell center :required="required" :title="label">
<d-label :label="label" :help="fieldConfig.help" slot="title" />
<template #right-icon>
<span @click="changeVisible(true)">
<span v-if="multiple">
<van-tag v-for="(val, index) in currentValue" closeable size="medium" @close="del(val, index)" :key="val[lovConfig.valueField]">{{val[lovConfig.displayField]}}</van-tag>
</span>
<span v-if="!currentValue || currentValue.length === 0" class="placeholder">请选择</span>
<span v-else>{{currentValue[lovConfig.displayField || 'label']}}</span>
</span>
<van-icon name="arrow" style="margin-left: 5px" />
</template>
</van-cell>
<van-popup v-model="visible" round position="bottom" class="d-lov-popup">
<div class="title">
<span></span>
<span class="title-label">{{fieldConfig.description}}</span>
<span class="cancel" @click="changeVisible(false)"><van-icon name="cross" /></span>
</div>
<van-search
v-model="searchValue"
shape="round"
placeholder="搜索"
@search="onSearch"
/>
<scroll
ref="scroll"
:pullUp="true"
:height="multiple ? 'calc(100% - 165px)' : 'calc(100% - 105px)'"
@pullingUp="getLovData">
<ul class="list">
<li
v-for="item in lovData"
:class="value === item[lovConfig.valueField] ? 'item active' : 'item'"
:key="item[lovConfig.valueField]"
>
<fragment v-if="multiple">
<van-checkbox shape="square" v-model="item._selected" @click="selectData(item)">
<div class="item-content">
<fragment v-for="(td, index) in lovConfig.tableFields" :key="td.dataIndex">
<span v-if="index < 2">{{item[td.dataIndex]}}</span>
</fragment>
</div>
</van-checkbox>
</fragment>
<div class="item-content" v-else @click="selectData(item)">
<fragment v-for="(td, index) in lovConfig.tableFields" :key="td.dataIndex">
<span v-if="index < 2">{{item[td.dataIndex]}}</span>
</fragment>
</div>
</li>
</ul>
</scroll>
<div class="footer" v-if="multiple">
<van-checkbox shape="square" v-model="selectAllFlag" @change="selectAllData">全选</van-checkbox>
<button type="primary" @click="test">确定</button>
</div>
</van-popup>
</div>
</template>
<script>
import {Cell, Switch, Icon, Popup, Search, Checkbox, Tag} from 'vant';
import DLabel from './DLabel';
import hlsHttp from '../utils/hlsHttp';
import utils from '../../scripts/hlsUtil';
export default {
name: 'DLov',
components: {
DLabel,
[Cell.name]: Cell,
[Switch.name]: Switch,
[Icon.name]: Icon,
[Popup.name]: Popup,
[Search.name]: Search,
[Checkbox.name]: Checkbox,
[Tag.name]: Tag,
},
props: {
fieldConfig: {
type: Object,
default: () => {}
},
label: {
type: String,
default: ''
},
value: {
type: Object | Array,
default: () => {},
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
},
multiple: { // 是否多选
type: Boolean,
default: false,
}
},
data () {
return {
lovConfig: {}, // lov配置
lovData: [],
searchValue: '', //搜索字段
selectAllFlag: false,
page: 0,
size: 10,
currentValue: null,
testValue: false,
visible: false,
options: [],
}
},
mounted() {
this.getLovConfig();
},
methods: {
getLovConfig() {
hlsHttp.get(`/hpfm/v1/${utils.getOrganizationId()}/lov-view/info`, {viewCode: this.fieldConfig.lovCode}).then(res => {
this.lovConfig = res;
// this.getLovData();
});
},
getLovData() {
const {queryUrl, lovCode} = this.lovConfig;
const params = {
lovCode,
page: this.page,
size: this.size,
tenantId: utils.getOrganizationId()
}
hlsHttp.get(queryUrl, params).then(res => {
this.lovData = this.lovData.concat(res.content.map(item => ({...item, _selected: false})));
if (res.totalElements > this.lovData.length) {
this.page = this.page + 1;
this.$refs.scroll.update(false)
} else {
this.$refs.scroll.update(true)
}
});
},
changeVisible(flag) {
if (!this.disabled) {
this.visible = flag;
if (flag && this.lovData.length === 0) {
this.getLovData()
}
}
},
onInput (val) {
this.$emit('input', val);
},
selectData(val) {
if(!this.multiple) {
this.currentValue = val;
this.changeVisible(false);
this.$emit('input', val[this.lovConfig.displayField]);
} else {
val._selected = true;
}
},
// 确定
test() {
this.changeVisible(false);
console.log('过来了 ')
const data = this.lovData.filter(o => o._selected);
this.currentValue = data;
this.$emit('input', data);
this.$emit('change', data)
},
// 删除
del(val, index) {
const newData = [...this.currentValue];
newData.splice(index, 1);
this.currentValue = [...newData];
this.$emit('input', this.currentValue);
this.$emit('change', this.currentValue);
const lindex = this.lovData.findIndex(o => o[this.lovConfig.valueField] === o[this.lovConfig.valueField]);
this.lovData[lindex]._selected = false;
},
onSearch() {
},
// 全选
selectAllData(flag) {
this.lovData = this.lovData.map(o => ({...o, _selected: flag}));
}
},
}
</script>
<style lang="less">
@import './index.less';
.d-lov-popup {
// display: flex;
// flex-direction: column;
overflow: hidden;
// min-height: 200px;
// max-height: 75%;
height: 70%;
.title {
padding: 12px 15px 5px 15px;
display: flex;
align-items: center;
justify-content: space-between;
.cancel {
color: #666666;
}
.confirm {
color: #3789FF;
}
}
.title-label {
font-family: PingFangSC-Regular;
font-size: 16px;
color: #131313;
letter-spacing: 0;
font-weight: 400;
}
.d-lov-scroll {
flex: 1;
overflow: hidden;
min-height: 300px;
}
.list {
padding: 0 16px;
.item {
font-size: 14px;
color: #383F45;
padding: 15px 0;
border-bottom: 1px solid #F1F0F5;
&.active {
font-size: 16px;
color: #131313;
}
.item-content {
display: flex;
span {
width: 50%;
padding: 0 8px;
text-align: left;
}
}
}
}
.footer {
// height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 15px;
box-shadow: 0px 0px 12px 0px rgba(230, 233, 241, 1);
}
.van-checkbox__label {
flex: 1;
}
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 11:58:04
* @Version: 1.0.0
* @Description: 动态渲染-Select
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="d-form-item d-select">
<van-cell
center
:required="required"
:class="disabled && 'van-field--disabled'"
>
<d-label :label="label" :help="fieldConfig.help" slot="title" />
<div @click="showSelectOption(true)" class="cell-value">
<input placeholder="请选择" :value="currentValue.meaning" readonly class="value-input"/>
<van-icon name="arrow" />
</div>
</van-cell>
<van-popup v-model="visible" round position="bottom" class="d-select-popup">
<!-- <div class="d-select-popup">
<div class="title">
<span class="cancel" @click="showSelectOption(false)">取消</span>
<span class="title-label">请选择{{fieldConfig.description}}</span>
<span class="confirm"></span>
</div>
<ul class="list">
<li
v-for="option in options"
:class="value === option.value ? 'item active' : 'item'"
:key="option.value"
@click="selectOption(option)"
>{{option.meaning}}</li>
</ul>
</div> -->
<van-picker
show-toolbar
:title="`请选择${fieldConfig.description}`"
:columns="columns"
@cancel="visible = false"
@confirm="onConfirm"
ref="pickerRef"
/>
</van-popup>
</div>
</template>
<script>
import {Cell, Switch, Icon, Popup, Picker} from 'vant';
import DLabel from './DLabel';
import hlsHttp from '../utils/hlsHttp';
import utils from '../../scripts/hlsUtil';
export default {
name: 'DSelect',
components: {
DLabel,
[Picker.name]: Picker,
[Cell.name]: Cell,
[Switch.name]: Switch,
[Icon.name]: Icon,
[Popup.name]: Popup
},
props: {
fieldConfig: {
type: Object,
default: () => {}
},
label: {
type: String,
default: ''
},
value: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
}
},
data () {
return {
currentValue: {
meaning: '',
value: ''
},
visible: false,
options: [],
columns: [],
}
},
mounted() {
this.getOptions();
},
methods: {
getOptions() {
hlsHttp.get(`/hpfm/v1/${utils.getOrganizationId()}/lovs/data`, {lovCode: this.fieldConfig.lookupCode}).then(res => {
this.options = res;
this.columns = this.options.map(o => o.meaning)
if (this.value) {
const index = res.findIndex(o => o.value === this.value);
this.currentValue = index > -1 ? res[index] : {
meaning: '',
value: ''
}
this.$refs.pickerRef.setColumnIndex(index);
}
});
},
showSelectOption(flag) {
console.log('laile', flag)
if (!this.disabled) {
this.visible = flag;
}
},
onInput (val) {
this.$emit('input', val);
},
onConfirm() {
this.selectOption(this.options[this.$refs.pickerRef.getColumnIndex(0)]);
},
selectOption(val) {
this.currentValue = val;
this.showSelectOption(false);
this.$emit('input', val.value);
this.$emit('change', val.value);
}
},
}
</script>
<style lang="less">
@import './index.less';
.d-select-popup {
.van-picker__title {
color: #131313;
}
.van-picker__toolbar {
border-bottom: 1px solid #F1F0F5;
}
.title{
padding: 13px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #F1F0F5;
.cancel {
color: #666666;
}
.confirm {
color: #3789FF;
}
}
.title-label {
font-family: PingFangSC-Regular;
font-size: 16px;
color: #131313;
letter-spacing: 0;
font-weight: 400;
}
.list {
min-height: 200px;
max-height: 60%;
overflow: auto;
.item {
padding:6px 12px;
text-align: center;
font-size: 14px;
color: rgba(56,63,69,0.60);
&.active {
font-size: 16px;
color: #131313;
}
}
}
}
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 10:58:08
* @Version: 1.0.0
* @Description: 动态渲染-开关组件
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<van-cell center class="d-form-item" :required="required" :title="label">
<!-- <template #title>
<span v-if="required" class="required">*</span>
{{label}}
</template> -->
<template #right-icon>
<van-switch @change="checked" v-model="currentValue" :disabled="disabled" size="19" />
</template>
</van-cell>
</template>
<script>
import {Cell, Switch} from 'vant';
export default {
name: 'DSwitch',
components: {
[Cell.name]: Cell,
[Switch.name]: Switch,
},
props: {
label: {
type: String,
default: ''
},
value: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
}
},
data () {
return {
currentValue: false
}
},
watch: {
value: function(newValue, oldValue) {
if (newValue !== oldValue) {
this.currentValue = newValue
}
}
},
methods: {
checked (flag) {
if (!this.disable) {
this.$emit('input', flag)
this.$emit('change', flag)
}
},
},
}
</script>
<style lang="less">
@import './index.less';
</style>
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-01 09:55:12
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-07 10:57:58
* @Version: 1.0.0
* @Description: 动态渲染-超链接组件
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<van-cell center class="d-form-item d-url" :required="required" :title="label">
<!-- <template #title>
<span v-if="required" class="required">*</span>
{{label}}
</template> -->
<div class="d-url-value cell-value">
<img src="../assets/url-icon.png" alt="" class="right-icon" />
<a>{{value}}</a>
</div>
</van-cell>
</template>
<script>
import {Cell, Checkbox} from 'vant';
export default {
name: 'DUrl',
components: {
[Cell.name]: Cell,
[Checkbox.name]: Checkbox,
},
props: {
label: {
type: String,
default: ''
},
value: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
}
},
data () {
return {
currentValue: false
}
},
watch: {
value: function(newValue, oldValue) {
if (newValue !== oldValue) {
this.currentValue = newValue
}
}
},
methods: {
checked (flag) {
if (!this.disable) {
this.$emit('input', flag)
this.$emit('change', flag)
}
},
},
}
</script>
<style lang="less">
@import './index.less';
.d-url {
padding: 6px 14px;
.d-url-value {
padding: 5px 12px;
background: #3789FF10;
border-radius: 4px;
.right-icon {
margin-right: 8px;
}
}
}
</style>
.d-form-item {
position: relative;
.required {
color: red;
}
.placeholder {
text-align: right;
color: #B4B4B5;
}
.cell-value {
display: inline-flex;
align-items: center;
.right-icon {
width: 16px;
}
.value-input {
text-align: right;
&::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
color: #B4B4B5;
}
}
}
.van-icon {
color: #B4B4B5;
}
.van-cell__title {
min-width: 40%;
}
.van-field--disabled .van-cell__title {
color: #c8c9cc;
}
&:after {
position: absolute;
box-sizing: border-box;
content: " ";
pointer-events: none;
right: 4.267vw;
bottom: 0;
left: 4.267vw;
border-bottom: 1px solid #ebedf0;
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
}
}
\ No newline at end of file
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-31 15:09:30
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-08 22:15:10
* @Version: 1.0.0
* @Description: 页面按钮渲染
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<div class="layout-buttons" :id="id">
<!-- <div v-if="layoutButtons.length > num" class="layout-buttons-more">
<span class="layout-button-more" @click="more"><van-icon name="more-o" />更多</span>
<div class="layout-button-more-content" :style="{display: moreVisible ? 'flex' : 'none'}">
<van-button
v-for="btn in hideBtns"
round
type="info"
:key="btn.name"
:icon="buttonIcon[btn.name]"
class="layout-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
</div>
</div> -->
<van-popover
v-if="layoutButtons.length > num"
v-model="moreVisible"
trigger="click"
placement="top-start"
class="test"
:get-container="`#${id}`"
>
<div class="layout-button-more-content">
<van-button
v-for="btn in hideBtns"
round
:key="btn.name"
:icon="buttonIcon[btn.name]"
class="layout-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
</div>
<template #reference>
<span class="layout-button-more"><van-icon name="more-o" />更多</span>
</template>
</van-popover>
<van-button
v-for="btn in defaultBtns"
round
:key="btn.name"
:icon="buttonIcon[btn.name]"
class="layout-button"
@click="handlClick(btn)"
>{{btn.description}}</van-button>
</div>
</template>
<script>
import { Button, Icon, Popover } from 'vant';
import { v4 as uuidv4 } from 'uuid';
export default {
name: 'LayoutButtons',
components: {
[Button.name]: Button,
[Icon.name]: Icon,
[Popover.name]: Popover,
},
inject: ['config'],
data () {
return {
id: uuidv4(),
configInfo: {}, // 配置信息
layoutButtons: [], // 页面按钮
num: 2,
defaultBtns: [],
hideBtns: [],
moreVisible: false,
buttonIcon: {
create: 'plus',
save: 'description',
export: '',
remove: 'delete',
query: 'search'
}
}
},
mounted() {
this.configInfo = this.config();
this.layoutButtons = this.configInfo.layoutButtons || [];
// document.addEventListener('click', this.clickEle, false)
this.init();
},
destroyed () {
// document.removeEventListener('click', this.clickEle, false)
},
methods: {
// clickEle (e) {
// let content = document.querySelector('.layout-buttons-more')
// if ((content && !content.contains(e.target))) {
// this.moreVisible = false;
// }
// },
init() {
if(this.layoutButtons.length > this.num) {
this.defaultBtns = this.layoutButtons.slice(0, this.num);
this.hideBtns = this.layoutButtons.slice(this.num, this.layoutButtons.length);
} else {
this.defaultBtns = [...this.layoutButtons];
}
},
// more() {
// this.moreVisible = true;
// },
handlClick(btn) {
this.moreVisible = false;
console.log(btn.name)
}
},
}
</script>
<style scoped lang="less">
.layout-buttons {
padding: 10px;
display: flex;
position: relative;
align-items: center;
justify-content: space-around;
background: #FAFAFA;
box-shadow: 0px -2px 9px 0px rgba(226,226,226,1);
.layout-button {
min-width: 90px;
margin: 0 6px;
font-family: PingFangSC-Semibold;
font-size: 16px;
font-weight: 600;
}
.layout-button-more {
color: #666666;
font-size: 12px;
line-height: 17px;
display: flex;
color: #666;
margin: 0 6px;
flex-direction: column;
align-items: center;
.van-icon-more-o {
font-size: 16px;
}
}
.layout-button-more-content {
z-index: 1;
position: absolute;
left: -12px;
bottom: 100%;
display: flex;
flex-direction: column;
padding: 0 12px;
margin-bottom: 24px;
.layout-button {
margin-top: 12px;
min-width: 120px;
box-shadow: 0px 0px 18px 0px rgba(0,0,0,0.2);
border-radius: 20px;
white-space: nowrap;
// margin: 6px 0;
// box-shadow: 0px 2px 2px 0px #3789ff;
}
}
}
</style>
@primary-color: #3789FF;
\ No newline at end of file
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:51:56
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-08 17:48:08
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<h-view>
<h-header>
<h-return v-if="needBack" slot="left" @click.native="$routeGo()"/>
<div slot="center">{{configDatas.description}}</div>
</h-header>
<h-content>
<van-loading size="24px" vertical v-if="loading">加载中...</van-loading>
<config-render-component v-if="configDatas.id" />
</h-content>
<layout-buttons v-if="configDatas.id" />
</h-view>
</template>
<script>
import {Loading} from 'vant';
import LayoutButtons from './LayoutButtons';
import ConfigRenderComponent from './ConfigRenderComponent/init.jsx';
import {query, queryRoute} from './store'
import './index.less'
export default {
name: 'Dynamic',
components: {
[Loading.name]: Loading,
LayoutButtons,
ConfigRenderComponent
},
props: {
layoutCode: { // 页面编码
type: String,
default: ''
},
needBack: { // 是否需要返回键
type: Boolean,
default: true
},
},
data () {
return {
loading: false,
queryParams: {}, // 查询参数
qpara: {},
configDatas: {},
params: {},
config: {
datas: {},
params: {}
},
}
},
provide() {
return {
testname: () => this.testname,
config: () => this.configDatas // 提供方法,注意提供的方法要写在return对象中
};
},
created() {
window.localStorage.setItem('access_token', '38581e99-54c3-4252-9b4b-990f89576b08');
this.queryParams = {
...this.$route.params,
...this.$route.query
}
},
beforeMount() {
this.layoutCodeQuery();
},
methods: {
// 获取页面布局配置
async layoutCodeQuery() {
// const {disabled} = this.props;
this.loading = true;
let readOnly = false
// eslint-disable-next-line eqeqeq
if (this.queryParams.readOnly === "YES" || this.queryParams.readOnly == 'true') {
readOnly = true
}
// if (disabled) {
// readOnly = disabled
// }
this.qpara = {
...this.queryParams,
readOnly,
};
// if (this.store.config.datas !== null && isEqual(this.qpara, this.store.config.param)) {
// return;
// }
if (this.layoutCode) {
const {datas, params} = await query(
this.layoutCode,
this.qpara,
);
this.configDatas = datas;
this.params = params;
} else {
const {datas, params} = await queryRoute(
this.$route.path,
this.qpara,
);
this.configDatas = datas;
this.params = params;
}
this.loading = false;
}
},
}
</script>
<style scoped lang="less">
.content{
//background-color: #cfcfcf;
}
</style>
/*
* @Description: 布局抽象类
* @Author: huangtianyang
* @Date: 2020-01-13 15:56
*/
import { Bind } from 'lodash-decorators';
import React, { Component } from 'react';
/**
* @extends {Component}
* @ReactProps {React.ReactElement} components
*/
class AbstractLayout extends Component {
constructor(props) {
super(props);
this.counter = 0;
}
/**
* 包装渲染布局下组件
* @param {Array.<React.ReactElement>} components 组件
*/
@Bind()
packageContents(components) {
return components;
}
@Bind()
getCounter() {
this.counter += 1;
return this.counter;
}
/**
* 获取框架
* @param {*} content
* @returns {React.ReactElement}
*/
@Bind()
getLayoutFrame(content) {
return <div>{content}</div>;
}
render() {
const { components } = this.props;
return <>{this.getLayoutFrame(this.packageContents(components))}</>;
}
}
export default AbstractLayout;
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-30 17:19:32
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-07-30 17:34:57
* @Version: 1.0.0
* @Description: 布局抽象类
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
import Vue from 'vue';
import { Fragment } from 'vue-fragment';
const AbstractLayout = {
// functional: true,
// Props 是可选的
props: {
components: { // 是否需要返回键
type: Boolean,
default: true
},
staticLayoutTabs: {
type: Array,
default: () => [],
}
},
data () {
return {
counter: 0, // 配置信息
}
},
methods: {
/**
* 包装渲染布局下组件
* @param {Array.<VueElement>} components 组件
*/
packageContents(components) {
return components;
},
getCounter() {
this.counter += 1;
return this.counter;
},
/**
* 获取框架
* @param {*} content
* @returns {Element}
*/
getLayoutFrame(content) {
return <div>{content}</div>;
}
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (h, context) {
return <div></div>
return <Fragment>{this.getLayoutFrame(this.packageContents(components))}</Fragment>;
}
};
export default AbstractLayout;
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-02 11:36:24
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-05 15:45:37
* @Version: 1.0.0
* @Description: 导航栏
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
import Vue from 'vue';
import { v4 as uuidv4 } from 'uuid';
import { Tab, Tabs } from 'vant';
import './index.less';
const NavigationBar = {
name: 'NavigationBar',
components: {
[Tab.name]: Tab,
[Tabs.name]: Tabs
},
props: {
components: {
type: Array,
default: () => []
},
componentNames: {
type: Array,
default: () => []
}
},
data() {
return {
navId: uuidv4(),
navContentId: uuidv4(),
componentIds: [],
activeNav: '', // 默认显示nav
}
},
mounted () {
let ids = [];
this.components.forEach((com, index) => {
ids.push(`${this.componentNames[index].code}_${uuidv4()}`);
});
this.componentIds = ids;
this.activeNav = this.componentNames[0].code;
},
activated() {
// this.onScroll();
},
deactivated() {
},
methods: {
navClick(nav, index) {
this.activeNav = nav.code;
this.scrollToViewCenter(index);
this.scrollToPoint(index);
},
// 滚动到锚点
scrollToPoint(index) {
const target = document.getElementById(this.componentIds[index]);
if (target) {
// 滚动到目标位置
const top = target.offsetTop;
document.getElementById(this.navContentId).scrollTo({
top: top,
// behavior: 'smooth'
});
}
},
// 滚动到视图中心
scrollToViewCenter(index) {
const navEle = document.getElementById(`nav_${this.componentIds[index]}`);
if (navEle) {
const { left, width } = navEle.getBoundingClientRect();
const elCenter = left + width / 2; // 元素的中心高度
const center = window.innerWidth / 2; // 窗口的中心高度
const ele = document.getElementById(this.navId);
if (ele) {
ele.scrollTo({
left: ele.scrollLeft - (center - elCenter),
behavior: 'smooth'
})
}
}
},
// 滚动监听
onScroll(e) {
if (e.type === 'scroll') {
const top = e.target.scrollTop;
this.componentIds.forEach((o, index) => {
const ele = document.getElementById(o);
if (ele) {
const childTop = ele.offsetTop;
const nextEle = (index < this.componentIds.length - 1) ? document.getElementById(this.componentIds[index + 1]) : null;
if (nextEle) {
const childNextTop = nextEle.offsetTop;
if (childTop < top && top < childNextTop) {
if (this.activeNav !== this.componentNames[index].code) {
this.activeNav = this.componentNames[index].code;
this.scrollToViewCenter(index);
}
}
} else{
if (childTop < top) {
if (this.activeNav !== this.componentNames[index].code) { // 此处减少不必要的渲染
this.activeNav = this.componentNames[index].code;
this.scrollToViewCenter(index);
}
}
}
}
});
}
}
},
render: function (h, context) {
return (
<div class="d-navs">
<div class="d-nav" id={this.navId}>
{this.componentNames.map((name, index) => (
<span
id={`nav_${this.componentIds[index]}`}
class={this.activeNav === name.code ? "d-nav-item active" : "d-nav-item"}
onClick={() => this.navClick(name, index, false)}
>{name.label}</span>
))}
</div>
<div class="d-nav-content" id={this.navContentId} onScroll={this.onScroll}>
{this.components.map((component, index) => (
<div id={this.componentIds[index]}>{component}</div>
))}
</div>
</div>
)
}
}
export default NavigationBar;
@import '../../index.less';
.d-navs {
height: 100%;
display: flex;
flex-flow: column;
.d-nav {
overflow-x: auto;
white-space: nowrap;
display: flex;
align-items: center;
flex-wrap: nowrap;
background-color: #ffffff;
&::-webkit-scrollbar {
display: none;
}
.d-nav-item {
padding: 12px 20px 6px 20px;
font-family: PingFangSC-Regular;
font-size: 15px;
color: #B1B5C1;
line-height: 26px;
}
.active {
position: relative;
font-family: PingFangSC-Semibold;
color: @primary-color;
&::after {
content: '';
border-bottom: 2px solid @primary-color;
width: 42px;
display: inline-block;
position: absolute;
bottom: 0;
left: 50%;
margin-left: -21px;
}
}
}
.d-nav-content {
flex: 1;
overflow: auto;
position: relative;
}
}
\ No newline at end of file
/*
* @Description: In User Settings Edit
* @Author: gaoyang
* @Date: 2020-05-26 15:57
*/
import AbstractLayout from '../AbstractLayout';
export default class TabPane extends AbstractLayout {
/**
* @override
* @param {React.ReactElement} content
* @returns {*}
*/
getLayoutFrame(content) {
return content;
}
/**
* @override
* @param components 组件
* @returns {*[]}
*/
packageContents(components) {
return components;
}
}
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-02 11:06:33
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-06 12:03:35
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
import Vue from 'vue';
import { Tab, Tabs } from 'vant';
import './index.less';
const DTabs = {
name: 'DTabs',
components: {
[Tab.name]: Tab,
[Tabs.name]: Tabs
},
props: {
components: {
type: Array,
default: () => []
},
componentNames: {
type: Array,
default: () => []
}
},
render: function (h, context) {
return (
<van-tabs type="card" class="d-tabs" swipe-threshold="3">
{this.components.map((component, index) => (
<van-tab
title={this.componentNames[index].label}
key={this.componentNames[index].code}
>
{component}
</van-tab>
))}
</van-tabs>
)
}
}
export default DTabs;
.d-tabs {
height: 100%;
display: flex;
flex-direction: column;
.van-tabs__wrap {
height: auto;
padding: 7px 0;
background-color: #fff;
.van-tabs__nav--card {
border: 0 !important;
}
.van-tab {
background: rgba(55,137,255,0.10);
border-radius: 15px;
font-family: PingFangSC-Regular;
color: #B1B5C1;
border-right: 0;
margin: 0 6px;
padding: 0 22px;
}
.van-tab--active {
color: #fff;
background-color: @primary-color;
}
}
.van-tabs__content {
flex: 1;
overflow: auto;
.van-tab__pane {
height: 100%;
overflow: auto;
}
}
}
\ No newline at end of file
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-08-02 11:06:33
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-02 11:31:30
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
import Vue from 'vue';
const VBox = {
name: 'VBox',
props: {
components: {
type: Array,
default: () => []
}
},
render: function (h, context) {
return <fragment>{this.components}</fragment>
}
}
export default VBox;
{/* <template>
<fragment ><slot></slot></fragment>
</template>
<script>
export default {
name: 'VBox',
}
</script> */}
/*
* @Description: In User Settings Edit
* @Author: huangtianyang
* @Date: 2020-03-09 12:16
*/
import { Bind } from 'lodash-decorators';
import { debounce } from 'lodash';
import NavigationContentItem from './NavigationContentItem';
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
/**
*
* @param components {Array,<React.ReactElement>} -组件数组
* @param componentNames {Array.<String>} -组件描述名
* @returns {*}
* @constructor
*/
class NavigationContent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeIndex: 0,
windowHeight: window.innerHeight
};
this.ref = React.createRef();
this.scrollTops = [];
this.nodes = [];
this.content = null;
this.disableScrollHandler = false;
}
@Bind()
resizeHandler() {
if (window.parent === window.self) {
this.setState({ windowHeight: window.innerHeight });
}
}
componentDidMount() {
window.addEventListener('resize', this.resizeHandler);
const node = this.ref.current;
if (node) {
const { children } = node;
this.content = node;
for (let i = 0; i < children.length; i++) {
this.nodes.push(children[i]);
this.scrollTops.push(children[i].offsetTop - node.offsetTop);
}
}
}
componentWillUnmount() {
window.removeEventListener('resize', this.resizeHandler);
}
@Bind()
wheelHandler() {
this.disableScrollHandler = false;
}
@Bind()
getIndex(index) {
this.setState({ activeIndex: index });
}
@Bind()
getScrollTop(index) {
return this.nodes[index].offsetTop - this.content.offsetTop;
}
render() {
const { windowHeight, activeIndex } = this.state;
const { componentNames, components } = this.props;
return (
<div
className={classNames(styles['navigation-content'])}
style={{ maxHeight: windowHeight - 210 }}
>
<div
ref={this.ref}
onWheel={this.wheelHandler}
onScroll={event => {
if (!this.disableScrollHandler) {
debounce(e => {
e.persist();
const { scrollTop } = this.ref.current;
for (let i = 0; i < this.scrollTops.length; i++) {
const minTop = this.scrollTops[i];
const maxTop = this.scrollTops[i + 1];
if (
(scrollTop >= minTop && maxTop && scrollTop < maxTop) ||
(scrollTop >= minTop && !maxTop)
) {
this.setState({ activeIndex: i });
}
}
}, 100)(event);
}
}}
className={styles.content}
>
{components.map((component, index) => {
return (
<NavigationContentItem
parentRef={this.ref}
key={
/* eslint-disable-next-line react/no-array-index-key */
index
}
component={component}
index={index}
getIndex={() => this.getIndex(index)}
/>
);
})}
</div>
<div className={styles.menus}>
{componentNames.map((name, index) => {
return (
<div
key={name}
onClick={() => {
// eslint-disable-next-line no-undef
if (this.ref.current) {
this.ref.current.scrollTop = this.getScrollTop(index);
this.disableScrollHandler = true;
this.setState({ activeIndex: index });
}
}}
className={classNames(styles.menu, index === activeIndex ? styles.active : null)}
>
<div className={classNames(styles.menuCro, index === activeIndex ? styles.active : null,index===componentNames.length-1?styles.menuCroLast:null)} />
<span>{name}</span>
</div>
);
})}
</div>
</div>
);
}
}
export default NavigationContent;
/*
* @Description: In User Settings Edit
* @Author: huangtianyang
* @Date: 2020-03-09 22:21
*/
import React from 'react';
import classNames from 'classnames';
import styles from './index.less';
const NavigationContentItem = ({ component, index, getIndex }) => {
return (
<div
className={classNames(styles.item)}
onClick={() => {
getIndex(index);
}}
>
{component}
</div>
);
};
export default NavigationContentItem;
/*
* @Description: Server Config
* @Author: huangtianyang
* @Date: 2020-03-09 12:11
*/
import NavigationContent from './NavigationContent';
import React from 'react';
import styles from './index.less';
const NavigationBar = ({ components, componentNames }) => {
return (
<div className={styles['hls-navigation-bar']}>
<NavigationContent components={components} componentNames={componentNames} />
</div>
);
};
export default NavigationBar;
@import '~leaf-ui/lib/style/themes/default.less';
.hls-navigation-bar {
.navigation-content {
display: flex;
margin: 8px 0 0 0;
.menus {
flex-shrink: 0;
height: 100%;
background: #fff;
overflow: auto;
padding: 0 5px 0 10px;
width: 150px;
.menu {
height: 35px;
line-height: 35px;
margin: 4px 6px;
cursor: pointer;
font-family: @font-family;
font-weight: bold;
font-size: 13px;
color: #6a6a6a;
letter-spacing: 0.59px;
text-align: center;
display: flex;
align-items: center;
&.active {
color: @leaf-primary-color;
border-radius: 4px;
}
span{
margin-left: 10px;
width: 100px;
max-width: 100px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
}
.menuCro {
width: 10px;
height: 10px;
border-radius: 10px;
border: 2px solid rgba(0,0,0,.2);
position: relative;
&::after {
position: absolute;
content: '';
height: 30px;
border: 1px solid rgba(0,0,0,.2);
margin-top: 8px;
margin-left: -1px;
}
&.active {
border: 2px solid @leaf-primary-color;
}
}
.menuCroLast{
&::after{
position: absolute;
content: '';
height: 0;
border: 0;
}
}
}
.content {
width: 100%;
overflow-x: hidden;
overflow-y: auto;
padding: 0 8px;
.item {
margin: 5px 0;
}
}
}
}
/*
* @Description: 布局维护
* @Author: huangtianyang
* @Date: 2020-01-13 16:11
*/
import HBox from './VBox/index.jsx';
import VBox from './VBox/index.jsx';
import NavigationBar from './NavigationBar/index.jsx';
import TabPane from './VBox/index.jsx';
import Tabs from './Tabs/index.jsx';
export default { VBox, HBox, Tabs, TabPane, NavigationBar };
/*
* @Description: In User Settings Edit
* @Author: huangtianyang
* @Date: 2020-01-06 13:32
*/
import utils from '../../scripts/hlsUtil';
import {get} from '../utils/hlsHttp'
// lowcode/lowcode_page_config/lowcode_page_tab/FYSQD_NEW/10141?typeId=1305852827234414594&documentCategory=801009
export async function query(layoutCode, param) {
let config = {
datas: {},
param: {},
};
try {
const data = await get(`/hllc/v1/${utils.getOrganizationId()}/doc-layouts/config/app/${layoutCode}`, param);
config.param = param;
config.datas = data || {};
return config;
} catch (err) {
return config;
}
}
export async function queryRoute(match, param) {
let config = {
datas: {},
param: {},
};
try {
const res = await get(
`/hllc/v1/${getOrganizationId()}/rule-engine-low-code?route=${window.encodeURIComponent(
match
)}`
);
if (res) {
const data = await this.query(res.routeResultValue, param);
return data;
} else {
return config;
}
} catch (err) {
return config;
}
}
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-30 14:14:45
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-07-30 14:14:47
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
export default {
FIELD_READONLY: 'readOnly',
INPUT_MODE_DISPLAY_READONLY: 'READONLY',
INPUT_MODE_DISPLAY_REQUIRED: 'REQUIRED',
INPUT_DATATYPE_BOOLEAN: 'boolean',
INPUT_DATATYPE_NUMBER: 'number',
HISTORY_FIELD_STATUS_NEW: 'new',
HISTORY_FIELD_STATUS_UPDATE: 'update',
HISTORY_FIELD_STATUS_CLEAN: 'clean',
HISTORY_FIELD_STATUS_DELETE: 'delete'
};
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 16:36:23
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-02 10:52:06
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
// 引入axios
import axios from 'axios'
import {hlsPopup} from '../../../packages/index'
import qs from 'qs'
let promiseArr = {}
let cancel = {}
const CancelToken = axios.CancelToken
const withTokenAxios = axios.create({
transformResponse(data) {
// 对 data 进行任意转换处理
// eslint-disable-next-line no-undef
try {
return JSON.parse(data);
// return JSON.parse(JSON.stringify(JSONbig.parse(data)));
} catch (e) {
return data;
}
},
});
// 请求拦截器
withTokenAxios.interceptors.request.use(config => {
// 发起请求时,取消掉当前正在进行的相同请求
// config.cancelToken = new CancelToken(c => {
// cancel = c
// })
// if (promiseArr[config.url]) {
// promiseArr[config.url]('操作取消')
// promiseArr[config.url] = cancel
// } else {
// promiseArr[config.url] = cancel
// }
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截器即异常处理
withTokenAxios.interceptors.response.use(response => {
// if ($config.debug) {
// let postName = 'post'
// console.log(postName + ' success', response.config.url)
// console.log(postName + ' response ', response.data)
// console.log(postName + ' End!')
// }
const { status, data } = response;
if (status === 204) {
// return response;
return null;
}
if (data && data.failed) {
hlsPopup.hideLoading()
const err = {}
err.message = response.data.message
hlsPopup.showError(err.message)
throw err;
} else {
return response.data;
}
}, err => {
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '错误请求'
break
case 401:
err.message = '登录已失效,请重新登录'
break
case 403:
err.message = '拒绝访问'
break
case 404:
err.message = '请求错误,未找到该资源'
break
case 405:
err.message = '不支持的请求类型'
break
case 408:
err.message = '请求超时'
break
case 500:
err.message = '服务器端出错'
break
case 501:
err.message = '网络未实现'
break
case 502:
err.message = '网络错误'
break
case 503:
err.message = '服务不可用'
break
case 504:
err.message = '网络超时'
break
case 505:
err.message = 'http版本不支持该请求'
break
default:
err.message = `连接错误${err.response.status}`
}
} else {
err.message = '连接到服务器失败'
}
if (err.response && err.response.status === 401) {
hlsPopup.hideLoading()
hlsPopup.showPopup({
title: '登录失效,重新登录',
onConfirm: () => {
},
})
} else {
hlsPopup.hideLoading()
hlsPopup.showError(err.message + '<br/>' + (err.response.data ? err.response.data.message : ''))
}
throw err;
})
withTokenAxios.defaults.baseURL = ''
withTokenAxios.defaults.timeout = 10000
withTokenAxios.defaults.paramsSerializer = (params) => {
return qs.stringify(params, {arrayFormat: 'repeat'})
}
// get请求
export function get (url, param = {}) {
let headers = {}
if (window.localStorage.access_token) {
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + window.localStorage.access_token,
}
} else {
headers = {
'Content-Type': 'application/json',
}
}
// if ($config.debug) {
// let postName = 'GET'
// console.log(postName + ' Start!')
// console.log(postName + ' url ' + url)
// }
return new Promise((resolve, reject) => {
withTokenAxios({
method: 'get',
url: process.env.apiPath + url,
headers: headers,
params: param,
}).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
// post请求
export function post (url, param = {}) {
param.user_id = window.localStorage.user_id
param.access_token = window.localStorage.access_token
let headers = {}
if (window.localStorage.access_token) {
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + window.localStorage.access_token,
}
} else {
headers = {
'Content-Type': 'application/json',
}
}
// if ($config.debug) {
// let postName = 'POST'
// console.log(postName + ' Start!')
// console.log(postName + ' url ' + url)
// console.log(postName + ' parameter ' + JSON.stringify(param, '', 2))
// }
return new Promise((resolve, reject) => {
withTokenAxios({
method: 'post',
headers: headers,
url: process.env.apiPath + url,
data: param,
}).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
export default {
get: get,
post: post,
}
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-30 14:39:47
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-07-30 14:39:49
* @Version: 1.0.0
* @Description: 工具类
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
const getCustomizedProp = (layoutCode, tabCode, columnName, props) => {
return props.filter(
value =>
value.columnName === columnName &&
(!value.tabCode || value.tabCode === tabCode) &&
(!value.layoutCode || value.layoutCode === layoutCode)
);
};
const getCustomized = (layoutCode, tabCode, columnName, props) => {
const customizedProp = getCustomizedProp(layoutCode, tabCode, columnName, props);
return customizedProp.length > 0 ? customizedProp[0] : {};
};
const getHeaderExtrasProp = (layoutCode, tabCode, props) => {
return props.filter(
value =>
(!value.tabCode || value.tabCode === tabCode) &&
(!value.layoutCode || value.layoutCode === layoutCode)
);
};
export { getCustomizedProp, getCustomized,getHeaderExtrasProp };
@primary-color: #3789FF;
// popup
@popup-round-border-radius: 8px;
// button
@button-default-background-color: #f2f2f2;
@button-default-color: rgba(19,19,19,0.80);
// picker组件
@text-link-color: @primary-color;
@gray-6: #666666;
// tabs组件
// @tabs-card-height: 44px;
@tabs-default-color: @primary-color;
// cell组件
@cell-text-color: #656464;
@cell-vertical-padding: 12px;
@cell-label-color: #656464;
// popover
@popover-border-radius: 4px;
@popover-action-width: auto;
@popover-action-font-size: 12px;
@popover-light-text-color: rgba(19,19,19,0.80);
\ No newline at end of file
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:10:04
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-06 11:58:48
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
......@@ -7,11 +16,16 @@ import axios from 'axios'
import App from './App'
import router from './router/index'
// 样式
// 引入全部样式
import 'vant/lib/index.less';
// import flexible from './common/ydui.flexible'
import {componentInstall, hlsPopup, directives, filter, prototype} from '../packages/index'
import component from './components/index'
import Dynamic from './Dynamic';
/**
* http
......@@ -22,6 +36,11 @@ import {post, get} from './scripts/hlsHttp'
import hlsUtil from './scripts/hlsUtil'
import Fragment from 'vue-fragment'
Vue.use(Fragment.Plugin)
Vue.component('Dynamic', Dynamic);
Vue.use(prototype)
Vue.use(component)
......
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:10:04
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-08-01 18:54:39
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
/**
* @Author think
* @Date 2020-08-25 17:03
......@@ -14,6 +23,7 @@
</s-tab>
<scroll
ref="scroll" class="has-footer" :pullUp="true" :pull-down="true" @pullingUp="loadMore"
height="200px"
@pullingDown="loadMore">
<list-item>
<item v-for="item in list" :key="item">
......@@ -32,7 +42,7 @@ export default {
name: 'ScrollDemo',
data () {
return {
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
page: 1,
}
},
......
<!--
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-31 18:41:49
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-07-31 18:53:12
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
-->
<template>
<Dynamic layoutCode="BP001F1" />
</template>
<script>
export default {
name: 'DynamicDetail',
data () {
return {}
},
methods: {},
}
</script>
<style lang="less">
h4{
font-size: 20px;
margin-left: 15px;
}
a{
width: 100%;
}
</style>
<template>
<Dynamic layoutCode="BP001" />
</template>
<script>
export default {
name: 'TestDynamic',
data () {
return {}
},
methods: {},
}
</script>
<style lang="less">
h4{
font-size: 20px;
margin-left: 15px;
}
a{
width: 100%;
}
</style>
/*
* @Author: zong.wang01@hand-china.com
* @Date: 2024-07-29 10:10:04
* @LastEditors: zong.wang01@hand-china.com
* @LastEditTime: 2024-07-31 18:52:20
* @Version: 1.0.0
* @Description:
* @Copyright: Copyright (c) 2021, Hand-RongJing
*/
import Vue from 'vue'
import Router from 'vue-router'
......@@ -5,6 +14,8 @@ import Router from 'vue-router'
import Radio from '@/pages/radioTest'
import Form from '@/pages/form'
const TestDynamic = resolve => require.ensure([], () => { resolve(require('@/pages/testDynamic')) }, 'testDynamic')
const DynamicDetail = resolve => require.ensure([], () => { resolve(require('@/pages/dynamicDetail')) }, 'dynamicDetail')
const Demo = resolve => require.ensure([], () => { resolve(require('@/pages/demo')) }, 'demo')
const Home = resolve => require.ensure([], () => { resolve(require('@/pages/home')) }, 'home')
const HlsPopup = resolve => require.ensure([], () => { resolve(require('@/pages/hlsPopup')) }, 'hlsPopup')
......@@ -34,8 +45,10 @@ export default new Router({
routes: [
{
path: '/',
redirect: '/demo',
redirect: '/testDynamic',
},
{path: '/testDynamic', component: TestDynamic, name: 'TestDynamic', meta: {keepAlive: true}},
{path: '/dynamicDetail', component: DynamicDetail, name: 'DynamicDetail', meta: {keepAlive: true}},
{path: '/demo', component: Demo, name: 'Demo', meta: {keepAlive: true}},
{path: '/home', component: Home, name: 'Home', meta: {keepAlive: false}},
// test工具类
......
......@@ -874,4 +874,63 @@ export default {
}
},
getOrganizationId: function () {
return 0;
// const globalState = getDvaApp()._store.getState();
// const user = globalState.user || {};
// switch (system) {
// case 'hls': // return getCurrentOrganizationId();
// case 'hcf':
// // return 0;
// return user?.currentUser.tenantId;
// default:
// break;
// }
},
getCurrentUser: function () {
return {
"id": 1,
"loginName": "admin",
"email": "admin@hzero.com",
"organizationId": 0,
"realName": "超级管理员",
"phone": "18666666661",
"internationalTelCode": "+86",
"imageUrl": "http://hlsapp.hand-china.com/hls-file/public/hiam02/0/fd6ca1bdf3254e499b568f15cb8d3f51@WechatIMG775.jpg",
"language": "zh_CN",
"languageName": "简体中文",
"timeZone": "GMT+8",
"lastPasswordUpdatedAt": "2024-06-05 11:38:35",
"countryId": 1,
"countryName": "安道尔",
"regionId": 0,
"phoneCheckFlag": 1,
"emailCheckFlag": 1,
"passwordResetFlag": 1,
"tenantName": "融租易",
"tenantNum": "HLS",
"dateFormat": "YYYY-MM-DD",
"timeFormat": "HH:mm:ss",
"dateTimeFormat": "YYYY-MM-DD HH:mm:ss",
"changePasswordFlag": 0,
"title": "融租易5.0",
"logo": "http://leafdev.hand-china.com/hls-file/public/hpfm05/0/d21ff00430f146caa234a8e5ddde2320@融租易icon.png",
"menuLayout": "inline",
"menuLayoutTheme": "default",
"roleMergeFlag": 0,
"tenantId": 0,
"currentRoleId": 1,
"currentRoleCode": "role/organization/default/administrator",
"currentRoleName": "租户管理员",
"currentRoleLevel": "organization",
"currentRoleLabels": [
"TENANT_ADMIN"
],
"favicon": "",
"dataHierarchyFlag": 0,
"recentAccessTenantList": []
};
}
}
......@@ -69,6 +69,7 @@
@popup-header-right-text-color:@theme-color;
@popup-header-left-text-padding: 15px;
@popup-header-right-text-padding: 15px;
@popup-round-border-radius: 8px;
/**
* actionsheet
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment