From 49da1f79aa5cd4acf1462d28922d4e8f80c00309 Mon Sep 17 00:00:00 2001 From: zhangherong <571457620@qq.com> Date: 星期一, 31 三月 2025 10:45:50 +0800 Subject: [PATCH] art: 设备管理-保养标准-新增 --- src/views/eam/base/modules/EamMaintenanceStandardModal.vue | 278 +++++++++++++++++++++++ src/views/eam/base/EamMaintenanceStandardList.vue | 199 ++++++++++++++++ src/views/eam/equipment/modules/LxSearchEquipmentSelect.vue | 223 ++++++++++++++++++ 3 files changed, 700 insertions(+), 0 deletions(-) diff --git a/src/views/eam/base/EamMaintenanceStandardList.vue b/src/views/eam/base/EamMaintenanceStandardList.vue new file mode 100644 index 0000000..7d68d68 --- /dev/null +++ b/src/views/eam/base/EamMaintenanceStandardList.vue @@ -0,0 +1,199 @@ +<template> + <a-card :bordered="false"> + + <!-- 鏌ヨ鍖哄煙 --> + <div class="table-page-search-wrapper"> + <a-form layout="inline" @keyup.enter.native="searchQuery"> + <a-row :gutter="24"> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鏍囧噯缂栫爜"> + <a-input placeholder="璇疯緭鍏ユ爣鍑嗙紪鐮�" v-model="queryParam.standardCode"></a-input> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="鏍囧噯鍚嶇О"> + <a-input placeholder="璇疯緭鍏ユ爣鍑嗗悕绉�" v-model="queryParam.standardName"></a-input> + </a-form-item> + </a-col> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="璁惧缂栧彿"> + <a-input placeholder="璇疯緭鍏ヨ澶囩紪鍙�" v-model="queryParam.equipmentCode"></a-input> + </a-form-item> + </a-col> + <template v-if="toggleSearchStatus"> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <a-form-item label="淇濆吇鍒嗙被"> + <a-input placeholder="璇疯緭鍏ヤ繚鍏诲垎绫�" v-model="queryParam.maintenanceCategory"></a-input> + </a-form-item> + </a-col> + </template> + <a-col :xl="6" :lg="7" :md="8" :sm="24"> + <span style="float: left;overflow: hidden;" class="table-page-search-submitButtons"> + <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button> + <a-button type="info" @click="searchReset" icon="reload" style="margin-left: 8px">閲嶇疆</a-button> + <a @click="handleToggleSearch" style="margin-left: 8px"> + {{ toggleSearchStatus ? '鏀惰捣' : '灞曞紑' }} + <a-icon :type="toggleSearchStatus ? 'up' : 'down'" /> + </a> + </span> + </a-col> + + </a-row> + </a-form> + </div> + + <!-- 鎿嶄綔鎸夐挳鍖哄煙 --> + <div class="table-operator"> + <a-button @click="handleAdd" type="primary" icon="plus">鏂板</a-button> + <a-button type="primary" icon="download" @click="handleExportXls('淇濆吇鏍囧噯')">瀵煎嚭</a-button> + <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" + @change="handleImportExcel"> + <a-button type="primary" icon="import">瀵煎叆</a-button> + </a-upload> + <a-dropdown v-if="selectedRowKeys.length > 0"> + <a-menu slot="overlay"> + <a-menu-item key="1" @click="batchDel"> + <a-icon type="delete" /> + 鍒犻櫎 + </a-menu-item> + </a-menu> + <a-button style="margin-left: 8px"> 鎵归噺鎿嶄綔 + <a-icon type="down" /> + </a-button> + </a-dropdown> + </div> + + <!-- table鍖哄煙-begin --> + <div> + <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;"> + <i class="anticon anticon-info-circle ant-alert-icon"></i> 宸查�夋嫨 <a + style="font-weight: 600">{{ selectedRowKeys.length }}</a>椤� + <a style="margin-left: 24px" @click="onClearSelected">娓呯┖</a> + </div> + + <a-table + ref="table" + size="middle" + bordered + rowKey="id" + :columns="columns" + :dataSource="dataSource" + :pagination="ipagination" + :loading="loading" + class="j-table-force-nowrap" + :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" + @change="handleTableChange"> + + <span slot="action" slot-scope="text, record"> + <a @click="handleEdit(record)">缂栬緫</a> + + <a-divider type="vertical" /> + <a-dropdown> + <a class="ant-dropdown-link">鏇村 <a-icon type="down" /></a> + <a-menu slot="overlay"> + <a-menu-item> + <a-popconfirm title="纭畾鍒犻櫎鍚�?" @confirm="() => handleDelete(record.id)"> + <a>鍒犻櫎</a> + </a-popconfirm> + </a-menu-item> + </a-menu> + </a-dropdown> + </span> + + </a-table> + </div> + <!-- table鍖哄煙-end --> + + <!-- 琛ㄥ崟鍖哄煙 --> + <eamMaintenanceStandard-modal ref="modalForm" @ok="modalFormOk"></eamMaintenanceStandard-modal> + </a-card> +</template> + +<script> +import '@/assets/less/TableExpand.less' +import EamMaintenanceStandardModal from './modules/EamMaintenanceStandardModal' +import { JeecgListMixin } from '@/mixins/JeecgListMixin' + +export default { + name: 'EamMaintenanceStandardList', + mixins: [JeecgListMixin], + components: { + EamMaintenanceStandardModal + }, + data() { + return { + description: '淇濆吇鏍囧噯绠$悊椤甸潰', + // 琛ㄥご + columns: [ + { + title: '#', + dataIndex: '', + key: 'rowIndex', + width: 60, + align: 'center', + customRender: function(t, r, index) { + return parseInt(index) + 1 + } + }, + { + title: '鏍囧噯缂栫爜', + align: 'center', + dataIndex: 'standardCode' + }, + { + title: '鏍囧噯鍚嶇О', + align: 'center', + dataIndex: 'standardName' + }, + { + title: '淇濆吇鍛ㄦ湡', + align: 'center', + dataIndex: 'maintenancePeriod' + }, + { + title: '淇濆吇鍒嗙被', + align: 'center', + dataIndex: 'maintenanceCategory' + }, + { + title: '璁惧缂栧彿', + align: 'center', + dataIndex: 'equipmentCode' + }, + { + title: '鏍囧噯鐘舵��', + align: 'center', + dataIndex: 'standardStatus' + }, + { + title: '鏍囧噯鐗堟湰', + align: 'center', + dataIndex: 'standardVersion' + }, + { + title: '鎿嶄綔', + dataIndex: 'action', + align: 'center', + scopedSlots: { customRender: 'action' } + } + ], + url: { + list: '/eam/maintenanceStandard/list', + delete: '/eam/maintenanceStandard/delete', + deleteBatch: '/eam/maintenanceStandard/deleteBatch', + exportXlsUrl: 'eam/maintenanceStandard/exportXls', + importExcelUrl: 'eam/maintenanceStandard/importExcel' + } + } + }, + computed: { + importExcelUrl: function() { + return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}` + } + }, + methods: {} +} +</script> +<style scoped> +@import '~@assets/less/common.less'; +</style> \ No newline at end of file diff --git a/src/views/eam/base/modules/EamMaintenanceStandardModal.vue b/src/views/eam/base/modules/EamMaintenanceStandardModal.vue new file mode 100644 index 0000000..45e4c74 --- /dev/null +++ b/src/views/eam/base/modules/EamMaintenanceStandardModal.vue @@ -0,0 +1,278 @@ +<template> + <j-modal + :title="title" + :width="1200" + :visible="visible" + :confirmLoading="confirmLoading" + switchFullscreen + @ok="handleOk" + @cancel="handleCancel" + cancelText="鍏抽棴"> + + <a-spin :spinning="confirmLoading"> + <a-form-model ref="form" :model="model" :rules="validatorRules"> + <a-row :gutter="24"> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="standardCode" label="鏍囧噯缂栫爜"> + <a-input placeholder="缂栫爜绯荤粺鑷姩鐢熸垚" v-model="model.standardCode" disabled /> + </a-form-model-item> + </a-col> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="standardName" label="鏍囧噯鍚嶇О"> + <a-input placeholder="璇疯緭鍏ユ爣鍑嗗悕绉�" v-model="model.standardName" /> + </a-form-model-item> + </a-col> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="maintenanceCategory" label="淇濆吇鍒嗙被"> + <j-dict-select-tag dict-code="maintenance_category" placeholder="璇烽�夋嫨淇濆吇鍒嗙被" v-model="model.maintenanceCategory" /> + </a-form-model-item> + </a-col> + </a-row> + <a-row :gutter="24"> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="fileCode" label="鏂囦欢缂栧彿"> + <a-input placeholder="璇疯緭鍏ヤ繚鍏绘爣鍑嗘枃浠剁紪鍙�" v-model="model.fileCode"/> + </a-form-model-item> + </a-col> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="initialDate" label="鍒濆鏃ユ湡"> + <a-date-picker placeholder="璇烽�夋嫨鍒濆鏃ユ湡" v-model="model.initialDate" format="yyyy-MM-dd" style="width: 100%"/> + </a-form-model-item> + </a-col> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="maintenancePeriod" label="淇濆吇鍛ㄦ湡"> + <a-input-number v-model="model.maintenancePeriod" :min="1" :precision="0" style="width: 100%"/> + </a-form-model-item> + </a-col> + </a-row> + <a-row :gutter="24"> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="equipmentId" label="璁惧缂栧彿"> + <lx-search-equipment-select placeholder="璇疯緭鍏ヨ澶囩紪鍙锋垨鍚嶇О鎼滅储" v-model="model.equipmentId"></lx-search-equipment-select> + </a-form-model-item> + </a-col> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="standardStatus" label="鏍囧噯鐘舵��"> + <j-dict-select-tag dict-code="maintenance_standard_status" v-model="model.standardStatus" disabled/> + </a-form-model-item> + </a-col> + <a-col :span="8"> + <a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" prop="standardVersion" label="鏍囧噯鐗堟湰"> + <a-input-number v-model="model.standardVersion" style="width: 100%" disabled/> + </a-form-model-item> + </a-col> + </a-row> + <a-row :gutter="24"> + <a-col :span="8"> + <a-form-model-item + label="鍙傝�冩枃浠�" + :labelCol="labelCol" + :wrapperCol="wrapperCol" + prop="fileList"> + <lx-upload :returnUrl="false" + :isMultiple="false" + v-model="model.fileList" + biz="EamMaintenanceStandard"> + </lx-upload> + </a-form-model-item> + </a-col> + </a-row> + <a-row :gutter="24"> + <j-vxe-table + ref="editableDetailTable" + :rowNumber="true" + :rowSelection="true" + :bordered="true" + :alwaysEdit="true" + :toolbar="true" + :toolbarConfig="detail.toolbarConfig" + keep-source + :height="300" + :loading="detail.loading" + :dataSource="detail.dataSource" + :columns="detail.columns" + style="margin-top: 8px;"/> + </a-row> + </a-form-model> + </a-spin> + </j-modal> +</template> + +<script> + import { httpAction } from '@/api/manage' + import { JVxeTableModelMixin } from '@/mixins/JVxeTableModelMixin.js' + import { JVXETypes } from '@/components/jeecg/JVxeTable' + import LxSearchEquipmentSelect from '@views/eam/equipment/modules/LxSearchEquipmentSelect' + + export default { + name: "EamMaintenanceStandardModal", + mixins: [JVxeTableModelMixin], + components: { + LxSearchEquipmentSelect, + }, + data () { + return { + title:"鎿嶄綔", + visible: false, + model: {}, + labelCol: { + xs: { span: 24 }, + sm: { span: 5 }, + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 }, + }, + + confirmLoading: false, + validatorRules: { + standardName: [ + { required: true, message: '璇疯緭鍏ユ爣鍑嗗悕绉�!' } + ], + maintenanceCategory: [ + { required: true, message: '璇烽�夋嫨淇濆吇鍒嗙被!' } + ], + initialDate: [ + { required: true, message: '璇烽�夋嫨鍒濆鏃ユ湡!' } + ], + maintenancePeriod: [ + { required: true, message: '璇疯緭鍏ヤ繚鍏诲懆鏈燂紝鍗曚綅锛氬ぉ!' } + ], + equipmentId: [ + { required: true, message: '璇烽�夋嫨璁惧!' } + ] + }, + url: { + add: "/eam/maintenanceStandard/add", + edit: "/eam/maintenanceStandard/edit", + }, + detail: { + loading: false, + dataSource: [], + columns: [ + { + title: 'ID', + key: 'id', + type: JVXETypes.hidden, + }, + { + title: 'standardId', + key: 'standardId', + type: JVXETypes.hidden, + }, + { + title: '搴忓彿', + key: 'itemCode', + type: JVXETypes.inputNumber, + width: '10%', + align:"center", + validateRules: [ + {required : true, unique: true, message: '搴忓彿涓嶈兘閲嶅'} + ] + }, + { + title: '淇濆吇椤�', + key: 'itemName', + type: JVXETypes.textarea, + width: '20%', + align:"center", + validateRules: [ + {required : true} + ] + }, + { + title: '瀛愪繚鍏婚」', + key: 'subItemName', + type: JVXETypes.textarea, + width: '25%', + align:"center", + }, + { + title: '淇濆吇瑕佹眰', + key: 'itemDemand', + type: JVXETypes.textarea, + width: '30%', + align:"center", + validateRules: [ + {required : true} + ] + }, + ], + toolbarConfig: { + // prefix 鍓嶇紑锛泂uffix 鍚庣紑 + slot: ['prefix', 'suffix'], + // add 鏂板鎸夐挳锛況emove 鍒犻櫎鎸夐挳锛沜learSelection 娓呯┖閫夋嫨鎸夐挳 + btn: ['add', 'remove', 'clearSelection'] + }, + } + } + }, + created () { + }, + methods: { + add () { + //鍒濆鍖栭粯璁ゅ�� + this.edit({}); + }, + edit (record) { + this.model = Object.assign({}, record); + this.visible = true; + }, + close () { + this.$emit('close'); + this.visible = false; + this.$refs.form.clearValidate(); + }, + async handleOk () { + const that = this; + let errMap = await that.$refs.editableDetailTable.validateTable(); + if(errMap){ + this.$message.warning("鏁版嵁鏍¢獙澶辫触锛�"); + return; + } + // 瑙﹀彂琛ㄥ崟楠岃瘉 + this.$refs.form.validate(valid => { + if (valid) { + debugger; + let tableData = that.$refs.editableDetailTable.getTableData(); + let newData = that.$refs.editableDetailTable.getNewData(); + let removeData = that.$refs.editableDetailTable.getDeleteData(); + + that.confirmLoading = true; + let httpurl = ''; + let method = ''; + if(!this.model.id){ + httpurl+=this.url.add; + method = 'post'; + }else{ + httpurl+=this.url.edit; + method = 'put'; + } + httpAction(httpurl,this.model,method).then((res)=>{ + if(res.success){ + that.$message.success(res.message); + that.$emit('ok'); + that.close(); + }else{ + that.$message.warning(res.message); + } + }).finally(() => { + that.confirmLoading = false; + }) + }else{ + return false; + } + }) + }, + handleCancel () { + this.close() + }, + + + } + } +</script> + +<style lang="less" scoped> + +</style> \ No newline at end of file diff --git a/src/views/eam/equipment/modules/LxSearchEquipmentSelect.vue b/src/views/eam/equipment/modules/LxSearchEquipmentSelect.vue new file mode 100644 index 0000000..4ec5444 --- /dev/null +++ b/src/views/eam/equipment/modules/LxSearchEquipmentSelect.vue @@ -0,0 +1,223 @@ +<template> + + <a-select + showSearch + labelInValue + :disabled="disabled" + :getPopupContainer="getParentContainer" + @search="loadData" + :placeholder="placeholder" + v-model="selectedAsyncValue" + style="width: 100%" + :filterOption="false" + @change="handleAsyncChange" + allowClear + :notFoundContent="loading ? undefined : null" + :mode="mode" + > + <template #suffixIcon> + <a-icon type="search" /> + </template> + <a-spin v-if="loading" slot="notFoundContent" size="small"/> + <a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option> + </a-select> + +</template> + +<script> +import { ajaxGetDictItems,getDictItemsFromCache } from '@/api/api' +import debounce from 'lodash/debounce'; +import { getAction } from '@/api/manage' + +export default { + name: 'LxSearchEquipmentSelect', + props:{ + disabled: Boolean, + value: [String, Number], + dictOptions: Array, + placeholder:{ + type:String, + default:"璇烽�夋嫨", + required:false + }, + pageSize:{ + type: Number, + default: 20, + required: false + }, + mode:{ + type: String, + default: '', + }, + }, + data(){ + this.loadData = debounce(this.loadData, 800);//娑堟姈 + this.lastLoad = 0; + return { + loading:false, + selectedValue:[], + selectedAsyncValue:[], + options: [], + } + }, + created(){ + this.initDictData(); + }, + watch:{ + "value":{ + immediate:true, + handler(val){ + if(!val){ + if(val==0){ + this.initSelectValue() + }else{ + this.selectedValue=[] + this.selectedAsyncValue=[] + } + }else{ + this.initSelectValue() + } + } + }, + }, + methods:{ + initSelectValue(){ + if(!this.selectedAsyncValue || !this.selectedAsyncValue.key || this.selectedAsyncValue.key!=this.value){ + console.log("杩欐墠璇锋眰鍚庡彴") + //update-begin-author:taoyan date:20220112 for: 鏂规硶initSelectValue 鏍规嵁涓嬫媺妗嗗疄闄呭�兼煡璇笅鎷夋鐨勬樉绀虹殑鏂囨湰 鍥犲悗鍙版帴鍙e彧澶勭悊3涓弬鏁帮紝鎵�浠ュ皢杩囨护鏉′欢鍘绘帀 + // TODO 闅愭偅 鏌ヨ鏁堢巼闂 杩樻槸搴旇鍦ㄥ悗鍙颁綔绛涢�� + let itemDictStr = this.dict + let arr = itemDictStr.split(',') + if(arr && arr.length==4){ + // 鍒犻櫎鏈�鍚庝竴涓厓绱� + arr.pop(); + itemDictStr = arr.join(',') + } + //update-end-author:taoyan date:20220112 for: 鏂规硶initSelectValue 鏍规嵁涓嬫媺妗嗗疄闄呭�兼煡璇笅鎷夋鐨勬樉绀虹殑鏂囨湰 鍥犲悗鍙版帴鍙e彧澶勭悊3涓弬鏁帮紝鎵�浠ュ皢杩囨护鏉′欢鍘绘帀 + getAction(`/eam/equipment/asyncLoadEquipment`,{key:this.value}).then(res=>{ + if(res.success){ + //update-begin---author:wangshuai ---date:20221115 for锛歔issues/4213]JSearchSelectTag鏀归�犳敮鎸佸閫�------------ + //鍒ゆ柇鏄惁澶氶�� + if(this.mode === 'multiple'){ + if(res.result && res.result.length>0){ + let itemArray = []; + let valueArray = this.value.split(",") + for (let i = 0; i < res.result.length; i++) { + itemArray.push({ + key:valueArray[i], + label:res.result[i] + }) + } + this.selectedAsyncValue = itemArray + }else{ + this.selectedAsyncValue = [] + this.selectedValue = [] + } + }else{ + let obj = { + key:this.value, + label:res.result + } + this.selectedAsyncValue = {...obj} + } + //update-end---author:wangshuai ---date:20221115 for锛歔issues/4213]JSearchSelectTag鏀归�犳敮鎸佸閫�-------------- + } + }) + } + }, + loadData(value){ + console.log("鏁版嵁鍔犺浇",value) + this.lastLoad +=1 + const currentLoad = this.lastLoad + this.options = [] + this.loading=true + // 瀛楀吀code鏍煎紡锛歵able,text,code + getAction(`/eam/equipment/asyncLoadEquipment`,{keyword:value, pageSize: this.pageSize}).then(res=>{ + this.loading=false + if(res.success){ + if(currentLoad!=this.lastLoad){ + return + } + this.options = res.result + console.log("鎴戞槸绗竴涓�",res) + }else{ + this.$message.warning(res.message) + } + + }) + + }, + initDictData(){ + //寮傛涓�寮�濮嬩篃鍔犺浇涓�鐐规暟鎹� + this.loading=true + getAction(`/eam/equipment/asyncLoadEquipment`,{pageSize: this.pageSize, keyword:''}).then(res=>{ + this.loading=false + if(res.success){ + this.options = res.result + }else{ + this.$message.warning(res.message) + } + }) + }, + filterOption(input, option) { + return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }, + handleChange (selectedValue) { + console.log("selectedValue",selectedValue) + this.selectedValue = selectedValue + this.callback() + }, + handleAsyncChange(selectedObj){ + //update-begin-author:scott date:20201222 for:銆愭悳绱€�戞悳绱㈡煡璇㈢粍浠讹紝鍒犻櫎鏉′欢锛岄粯璁や笅鎷夎繕鏄笂娆$殑缂撳瓨鏁版嵁锛屼笉濂� JT-191 + if(selectedObj){ + this.selectedAsyncValue = selectedObj + //update-begin---author:wangshuai ---date:20221115 for锛歔issues/4213]JSearchSelectTag鏀归�犳敮鎸佸閫�------------ + if(this.mode ==='multiple'){ + let keyArray = [] + for (let i = 0; i < selectedObj.length; i++) { + keyArray.push(selectedObj[i].key) + } + this.selectedValue = keyArray + }else{ + this.selectedValue = selectedObj.key + } + //update-end---author:wangshuai ---date:20221115 for锛歔issues/4213]JSearchSelectTag鏀归�犳敮鎸佸閫�------------ + }else{ + this.selectedAsyncValue = null + this.selectedValue = null + this.options = null + this.loadData("") + } + this.callback() + //update-end-author:scott date:20201222 for:銆愭悳绱€�戞悳绱㈡煡璇㈢粍浠讹紝鍒犻櫎鏉′欢锛岄粯璁や笅鎷夎繕鏄笂娆$殑缂撳瓨鏁版嵁锛屼笉濂� JT-191 + }, + callback(){ + //update-begin---author:wangshuai ---date:20221115 for锛歔issues/4213]JSearchSelectTag鏀归�犳敮鎸佸閫�------------ + if(this.mode === 'multiple'){ + this.$emit('change', this.selectedValue.join(",")); + }else{ + this.$emit('change', this.selectedValue); + } + //update-end---author:wangshuai ---date:20221115 for锛歔issues/4213]JSearchSelectTag鏀归�犳敮鎸佸閫�------------ + }, + getParentContainer(node){ + if(typeof this.getPopupContainer === 'function'){ + return this.getPopupContainer(node) + } else if(!this.popContainer){ + return node.parentNode + }else{ + return document.querySelector(this.popContainer) + } + }, + + }, + model: { + prop: 'value', + event: 'change' + } +} +</script> + +<style scoped> + +</style> \ No newline at end of file -- Gitblit v1.9.3