From ab8ff90598b99b1b88b925c03e82e20ff0426fcf Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 01 六月 2026 17:09:52 +0800
Subject: [PATCH] 新疆马铃薯 1.库存管理绑定设备,添加查看数采功能
---
src/views/inventoryManagement/stockManagement/New.vue | 40 ---
src/views/inventoryManagement/stockManagement/IotDataDialog.vue | 330 ++++++++++++++++++++++++++++++
src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue | 16
src/views/inventoryManagement/stockManagement/Record.vue | 39 +++
src/views/inventoryManagement/stockManagement/Subtract.vue | 22 +
src/api/inventoryManagement/stockInventory.js | 17 +
src/views/inventoryManagement/stockManagement/BindDeviceDialog.vue | 145 +++++++++++++
7 files changed, 558 insertions(+), 51 deletions(-)
diff --git a/src/api/inventoryManagement/stockInventory.js b/src/api/inventoryManagement/stockInventory.js
index 539eedc..0671391 100644
--- a/src/api/inventoryManagement/stockInventory.js
+++ b/src/api/inventoryManagement/stockInventory.js
@@ -103,3 +103,20 @@
});
};
+// 鑾峰彇鐗╄仈璁惧瀹炴椂鏁伴噰鏁版嵁
+export const getIotRealtimeData = (id) => {
+ return request({
+ url: `/stockInventory/iotRealtime/${id}`,
+ method: "get",
+ });
+};
+
+// 缁戝畾鐗╄仈璁惧鍒板簱瀛�
+export const bindIotDevice = (id, warehouse) => {
+ return request({
+ url: `/stockInventory/bindIotDevice/${id}`,
+ method: "put",
+ data: { warehouse },
+ });
+};
+
diff --git a/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue b/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
index b2fb88d..873e62a 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetApplication/index.vue
@@ -126,9 +126,9 @@
>
<el-option
v-for="person in employees"
- :key="person.id"
- :label="`${person.staffName}${person.postName ? ` (${person.postName})` : ''}`"
- :value="person.id"
+ :key="person.userId"
+ :label="`${person.nickName || person.userName}${person.dept?.deptName ? ` (${person.dept.deptName})` : ''}`"
+ :value="person.userId"
/>
</el-select>
</el-form-item>
@@ -156,7 +156,7 @@
import {ElMessage} from 'element-plus'
import {Plus, Document, Promotion, Bell} from '@element-plus/icons-vue'
import {getRoomEnum, saveMeetingApplication} from '@/api/collaborativeApproval/meeting.js'
-import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
+import {userListNoPageByTenantId} from "@/api/system/user.js";
// 褰撳墠鐢宠绫诲瀷
const currentType = ref('department') // approval: 瀹℃壒娴佺▼, department: 閮ㄩ棬绾�, notification: 閫氱煡鍙戝竷
@@ -415,12 +415,8 @@
getRoomEnum().then(res => {
meetingRooms.value = res.data
})
- staffOnJobListPage({
- current: -1,
- size: -1,
- staffState: 1
- }).then(res => {
- employees.value = res.data.records.sort((a, b) => (a.postName || '').localeCompare(b.postName || ''))
+ userListNoPageByTenantId().then(res => {
+ employees.value = res.data || []
})
})
</script>
diff --git a/src/views/inventoryManagement/stockManagement/BindDeviceDialog.vue b/src/views/inventoryManagement/stockManagement/BindDeviceDialog.vue
new file mode 100644
index 0000000..31a72c4
--- /dev/null
+++ b/src/views/inventoryManagement/stockManagement/BindDeviceDialog.vue
@@ -0,0 +1,145 @@
+<template>
+ <div>
+ <el-dialog v-model="isShow"
+ title="缁戝畾鐗╄仈璁惧"
+ width="600"
+ @close="closeModal">
+ <el-form label-width="100px"
+ :model="formState"
+ ref="formRef">
+ <el-form-item label="浜у搧鍚嶇О">
+ <el-input v-model="props.record.productName" disabled />
+ </el-form-item>
+ <el-form-item label="瑙勬牸鍨嬪彿">
+ <el-input v-model="props.record.model" disabled />
+ </el-form-item>
+ <el-form-item label="鎵瑰彿">
+ <el-input v-model="props.record.batchNo" disabled />
+ </el-form-item>
+ <el-form-item label="鐗╄仈璁惧"
+ prop="deviceIds"
+ :rules="[
+ {
+ required: false,
+ message: '璇烽�夋嫨鐗╄仈璁惧',
+ trigger: 'change',
+ }
+ ]">
+ <el-select v-model="formState.deviceIds"
+ multiple
+ filterable
+ placeholder="璇烽�夋嫨鐗╄仈璁惧"
+ style="width: 100%">
+ <el-option v-for="item in deviceOptions"
+ :key="item.id"
+ :label="`${item.deviceName} (${item.deviceModel})`"
+ :value="item.id">
+ <span style="float: left">{{ item.deviceName }}</span>
+ <span style="float: right; color: #8492a6; font-size: 13px">{{ item.deviceModel }}</span>
+ </el-option>
+ </el-select>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button type="primary" @click="handleSubmit" :loading="submitLoading">纭</el-button>
+ <el-button @click="closeModal">鍙栨秷</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, computed, onMounted, getCurrentInstance } from "vue";
+ import { getLedgerPage } from "@/api/equipmentManagement/ledger.js";
+ import { bindIotDevice } from "@/api/inventoryManagement/stockInventory.js";
+
+ const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+ record: {
+ type: Object,
+ default: () => ({}),
+ },
+ });
+
+ const emit = defineEmits(["update:visible", "completed"]);
+
+ const { proxy } = getCurrentInstance();
+
+ const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit("update:visible", val);
+ },
+ });
+
+ const formRef = ref(null);
+ const submitLoading = ref(false);
+ const deviceOptions = ref([]);
+
+ const formState = ref({
+ deviceIds: [],
+ });
+
+ // 鑾峰彇鐗╄仈璁惧鍒楄〃
+ const getDeviceOptions = async () => {
+ try {
+ const res = await getLedgerPage({
+ isIotDevice: 1,
+ page: 1,
+ size: 999,
+ });
+ if (res.data && res.data.records) {
+ deviceOptions.value = res.data.records;
+ }
+ } catch (error) {
+ console.error("鑾峰彇鐗╄仈璁惧鍒楄〃澶辫触:", error);
+ proxy.$modal.msgError("鑾峰彇鐗╄仈璁惧鍒楄〃澶辫触");
+ }
+ };
+
+ // 鍒濆鍖栧凡缁戝畾璁惧
+ const initSelectedDevices = () => {
+ if (props.record.warehouse) {
+ // warehouse 瀛楁瀛樺偍鐨勬槸閫楀彿鍒嗛殧鐨勮澶嘔D
+ const deviceIds = props.record.warehouse.split(",").map(id => Number(id.trim())).filter(id => !isNaN(id));
+ formState.value.deviceIds = deviceIds;
+ }
+ };
+
+ const closeModal = () => {
+ formState.value.deviceIds = [];
+ isShow.value = false;
+ };
+
+ const handleSubmit = () => {
+ formRef.value.validate(valid => {
+ if (valid) {
+ submitLoading.value = true;
+ // 灏嗚澶嘔D鏁扮粍杞崲涓洪�楀彿鍒嗛殧鐨勫瓧绗︿覆
+ const warehouse = formState.value.deviceIds.join(",");
+ bindIotDevice(props.record.id, warehouse)
+ .then(res => {
+ submitLoading.value = false;
+ proxy.$modal.msgSuccess("缁戝畾鎴愬姛");
+ closeModal();
+ emit("completed");
+ })
+ .catch(() => {
+ submitLoading.value = false;
+ });
+ }
+ });
+ };
+
+ onMounted(() => {
+ getDeviceOptions();
+ initSelectedDevices();
+ });
+</script>
diff --git a/src/views/inventoryManagement/stockManagement/IotDataDialog.vue b/src/views/inventoryManagement/stockManagement/IotDataDialog.vue
new file mode 100644
index 0000000..2f12009
--- /dev/null
+++ b/src/views/inventoryManagement/stockManagement/IotDataDialog.vue
@@ -0,0 +1,330 @@
+<template>
+ <div>
+ <el-dialog v-model="isShow"
+ title="鐗╄仈璁惧瀹炴椂鏁伴噰"
+ width="800"
+ @close="closeModal">
+ <div v-if="loading" v-loading="loading" element-loading-text="鍔犺浇涓�..." style="min-height: 200px;"></div>
+ <div v-else-if="!hasDevices" class="empty-state">
+ <el-empty description="鏆傛棤缁戝畾鐨勭墿鑱旇澶�">
+ <el-button type="primary" @click="closeModal">鍘荤粦瀹�</el-button>
+ </el-empty>
+ </div>
+ <div v-else>
+ <div class="device-header">
+ <div class="refresh-switch">
+ <el-switch v-model="autoRefresh" active-text="鑷姩鍒锋柊(30s)" />
+ </div>
+ <el-button type="primary" size="small" @click="fetchData" :loading="loading">
+ <el-icon><Refresh /></el-icon>鍒锋柊
+ </el-button>
+ </div>
+ <div class="devices-container">
+ <el-card v-for="device in deviceData.devices" :key="device.deviceId" class="device-card">
+ <template #header>
+ <div class="device-header-info">
+ <div class="device-title">
+ <span class="device-name">{{ device.deviceName }}</span>
+ <el-tag size="small" type="info">{{ device.deviceModel }}</el-tag>
+ </div>
+ <div class="device-status">
+ <span class="status-dot" :class="getStatusClass(device.status)"></span>
+ <span :class="getStatusTextClass(device.status)">{{ device.status || '鏈煡' }}</span>
+ </div>
+ </div>
+ </template>
+ <div v-if="device.status === '鍦ㄧ嚎'" class="device-data">
+ <el-row :gutter="10">
+ <el-col :span="8" v-if="device.temperature">
+ <div class="data-item">
+ <el-icon class="data-icon"><Sunny /></el-icon>
+ <div class="data-info">
+ <div class="data-label">娓╁害</div>
+ <div class="data-value">{{ device.temperature }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8" v-if="device.humidity">
+ <div class="data-item">
+ <el-icon class="data-icon"><Drizzling /></el-icon>
+ <div class="data-info">
+ <div class="data-label">婀垮害</div>
+ <div class="data-value">{{ device.humidity }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8" v-if="device.co2">
+ <div class="data-item">
+ <el-icon class="data-icon"><WindPower /></el-icon>
+ <div class="data-info">
+ <div class="data-label">CO2</div>
+ <div class="data-value">{{ device.co2 }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8" v-if="device.light">
+ <div class="data-item">
+ <el-icon class="data-icon"><Sunrise /></el-icon>
+ <div class="data-info">
+ <div class="data-label">鍏夌収</div>
+ <div class="data-value">{{ device.light }}</div>
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="8" v-if="device.battery">
+ <div class="data-item">
+ <el-icon class="data-icon"><Lightning /></el-icon>
+ <div class="data-info">
+ <div class="data-label">鐢甸噺</div>
+ <div class="data-value">{{ device.battery }}</div>
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+ </div>
+ <div v-else class="device-offline">
+ <el-alert :title="device.statusMessage || '璁惧绂荤嚎'" type="warning" :closable="false" show-icon />
+ </div>
+ </el-card>
+ </div>
+ </div>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="closeModal">鍏抽棴</el-button>
+ </div>
+ </template>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, computed, onMounted, onUnmounted, watch } from "vue";
+ import { getIotRealtimeData } from "@/api/inventoryManagement/stockInventory.js";
+ import { Refresh, Sunny, Drizzling, WindPower, Sunrise, Lightning } from "@element-plus/icons-vue";
+
+ const props = defineProps({
+ visible: {
+ type: Boolean,
+ required: true,
+ },
+ record: {
+ type: Object,
+ default: () => ({}),
+ },
+ });
+
+ const emit = defineEmits(["update:visible"]);
+
+ const isShow = computed({
+ get() {
+ return props.visible;
+ },
+ set(val) {
+ emit("update:visible", val);
+ },
+ });
+
+ const loading = ref(false);
+ const deviceData = ref({
+ inventoryId: null,
+ iotDeviceIds: "",
+ devices: [],
+ });
+ const autoRefresh = ref(false);
+ let refreshTimer = null;
+
+ const hasDevices = computed(() => {
+ return deviceData.value.devices && deviceData.value.devices.length > 0;
+ });
+
+ const getStatusClass = (status) => {
+ switch (status) {
+ case "鍦ㄧ嚎":
+ return "status-online";
+ case "offline":
+ return "status-offline";
+ case "error":
+ return "status-error";
+ default:
+ return "status-offline";
+ }
+ };
+
+ const getStatusTextClass = (status) => {
+ switch (status) {
+ case "鍦ㄧ嚎":
+ return "text-online";
+ case "offline":
+ return "text-offline";
+ case "error":
+ return "text-error";
+ default:
+ return "text-offline";
+ }
+ };
+
+ const fetchData = async () => {
+ if (!props.record.id) return;
+ loading.value = true;
+ try {
+ const res = await getIotRealtimeData(props.record.id);
+ if (res.code === 200 && res.data) {
+ deviceData.value = res.data;
+ }
+ } catch (error) {
+ console.error("鑾峰彇鐗╄仈璁惧鏁版嵁澶辫触:", error);
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const closeModal = () => {
+ autoRefresh.value = false;
+ isShow.value = false;
+ };
+
+ // 鑷姩鍒锋柊
+ watch(autoRefresh, (val) => {
+ if (val) {
+ refreshTimer = setInterval(() => {
+ fetchData();
+ }, 30000);
+ } else {
+ if (refreshTimer) {
+ clearInterval(refreshTimer);
+ refreshTimer = null;
+ }
+ }
+ });
+
+ onMounted(() => {
+ fetchData();
+ });
+
+ onUnmounted(() => {
+ if (refreshTimer) {
+ clearInterval(refreshTimer);
+ }
+ });
+</script>
+
+<style scoped>
+ .empty-state {
+ padding: 40px 0;
+ }
+
+ .device-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ }
+
+ .refresh-switch {
+ display: flex;
+ align-items: center;
+ }
+
+ .devices-container {
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ }
+
+ .device-card {
+ margin-bottom: 10px;
+ }
+
+ .device-header-info {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .device-title {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+
+ .device-name {
+ font-weight: bold;
+ font-size: 16px;
+ }
+
+ .device-status {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ }
+
+ .status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ display: inline-block;
+ }
+
+ .status-online {
+ background-color: #67c23a;
+ }
+
+ .status-offline {
+ background-color: #909399;
+ }
+
+ .status-error {
+ background-color: #f56c6c;
+ }
+
+ .text-online {
+ color: #67c23a;
+ }
+
+ .text-offline {
+ color: #909399;
+ }
+
+ .text-error {
+ color: #f56c6c;
+ }
+
+ .device-data {
+ padding: 10px 0;
+ }
+
+ .data-item {
+ display: flex;
+ align-items: center;
+ padding: 10px;
+ background-color: #f5f7fa;
+ border-radius: 4px;
+ margin-bottom: 10px;
+ }
+
+ .data-icon {
+ font-size: 24px;
+ color: #409eff;
+ margin-right: 10px;
+ }
+
+ .data-info {
+ flex: 1;
+ }
+
+ .data-label {
+ font-size: 12px;
+ color: #909399;
+ margin-bottom: 2px;
+ }
+
+ .data-value {
+ font-size: 16px;
+ font-weight: bold;
+ color: #303133;
+ }
+
+ .device-offline {
+ padding: 20px 0;
+ }
+</style>
diff --git a/src/views/inventoryManagement/stockManagement/New.vue b/src/views/inventoryManagement/stockManagement/New.vue
index 06d72bb..d49fc5a 100644
--- a/src/views/inventoryManagement/stockManagement/New.vue
+++ b/src/views/inventoryManagement/stockManagement/New.vue
@@ -49,23 +49,6 @@
value="unqualified" />
</el-select>
</el-form-item>
- <el-form-item label="浠撳簱"
- prop="warehouse"
- :rules="[
- {
- required: true,
- message: '璇烽�夋嫨浠撳簱',
- trigger: 'change',
- }
- ]">
- <el-select v-model="formState.warehouse"
- placeholder="璇烽�夋嫨浠撳簱">
- <el-option v-for="item in warehouseOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value" />
- </el-select>
- </el-form-item>
<el-form-item label="搴撳瓨鏁伴噺"
prop="qualitity">
<el-input-number v-model="formState.qualitity"
@@ -119,11 +102,10 @@
</template>
<script setup>
- import { ref, computed, watch, getCurrentInstance, onMounted } from "vue";
+ import { ref, computed, watch, getCurrentInstance } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import { addStockInRecordOnly } from "@/api/inventoryManagement/stockInventory.js";
import { createStockUnInventory } from "@/api/inventoryManagement/stockUninventory.js";
- import { getDicts } from "@/api/system/dict/data";
const props = defineProps({
visible: {
@@ -146,7 +128,6 @@
productName: "",
productModelName: "",
unit: "",
- warehouse: undefined,
type: undefined,
qualitity: 0,
batchNo: null,
@@ -154,9 +135,6 @@
createTime: "",
remark: "",
});
-
- // 浠撳簱閫夐」
- const warehouseOptions = ref([]);
const isShow = computed({
get() {
@@ -168,21 +146,6 @@
});
const showProductSelectDialog = ref(false);
-
- // 鑾峰彇浠撳簱瀛楀吀鏁版嵁
- const getWarehouseOptions = async () => {
- const res = await getDicts("warehouse");
- if (res.code === 200) {
- warehouseOptions.value = res.data.map(item => ({
- label: item.dictLabel,
- value: item.dictValue,
- }));
- }
- };
-
- onMounted(() => {
- getWarehouseOptions();
- });
// 鎵瑰彿涓虹┖鏃惰浆涓� null
watch(
@@ -204,7 +167,6 @@
productName: "",
productModelName: "",
unit: "",
- warehouse: undefined,
type: undefined,
qualitity: 0,
batchNo: null,
diff --git a/src/views/inventoryManagement/stockManagement/Record.vue b/src/views/inventoryManagement/stockManagement/Record.vue
index 13217f9..1ce95be 100644
--- a/src/views/inventoryManagement/stockManagement/Record.vue
+++ b/src/views/inventoryManagement/stockManagement/Record.vue
@@ -113,12 +113,18 @@
show-overflow-tooltip />
<el-table-column fixed="right"
label="鎿嶄綔"
- min-width="80"
+ min-width="190"
align="center">
<template #default="scope">
<el-button link
type="primary"
@click="showDetailModal(scope.row)">璇︽儏</el-button>
+ <el-button link
+ type="success"
+ @click="showBindDeviceModal(scope.row)">缁戝畾璁惧</el-button>
+ <el-button link
+ type="info"
+ @click="showIotDataModal(scope.row)">鏌ョ湅鏁伴噰</el-button>
</template>
</el-table-column>
</el-table>
@@ -156,6 +162,15 @@
:operation-type="operationType"
:type="record.stockType"
@completed="handleQuery" />
+ <!-- 缁戝畾鐗╄仈璁惧 -->
+ <bind-device-dialog v-if="isShowBindDeviceModal"
+ v-model:visible="isShowBindDeviceModal"
+ :record="record"
+ @completed="handleQuery" />
+ <!-- 鏌ョ湅鐗╄仈璁惧鏁伴噰 -->
+ <iot-data-dialog v-if="isShowIotDataModal"
+ v-model:visible="isShowIotDataModal"
+ :record="record" />
</div>
</template>
@@ -187,6 +202,12 @@
const BatchNoQtyDetail = defineAsyncComponent(() =>
import("@/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue")
);
+ const BindDeviceDialog = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/BindDeviceDialog.vue")
+ );
+ const IotDataDialog = defineAsyncComponent(() =>
+ import("@/views/inventoryManagement/stockManagement/IotDataDialog.vue")
+ );
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const selectedRows = ref([]);
@@ -209,6 +230,10 @@
const operationType = ref("frozen");
// 鏄惁鏄剧ず瀵煎叆寮规
const isShowImportModal = ref(false);
+ // 鏄惁鏄剧ず缁戝畾璁惧寮规
+ const isShowBindDeviceModal = ref(false);
+ // 鏄惁鏄剧ず鐗╄仈鏁伴噰寮规
+ const isShowIotDataModal = ref(false);
const data = reactive({
searchForm: {
productName: "",
@@ -308,6 +333,18 @@
operationType.value = "thaw";
};
+ // 鐐瑰嚮缁戝畾璁惧
+ const showBindDeviceModal = row => {
+ record.value = row;
+ isShowBindDeviceModal.value = true;
+ };
+
+ // 鐐瑰嚮鏌ョ湅鏁伴噰
+ const showIotDataModal = row => {
+ record.value = row;
+ isShowIotDataModal.value = true;
+ };
+
// 琛ㄦ牸閫夋嫨鏁版嵁
const handleSelectionChange = selection => {
// 杩囨护鎺夊瓙鏁版嵁
diff --git a/src/views/inventoryManagement/stockManagement/Subtract.vue b/src/views/inventoryManagement/stockManagement/Subtract.vue
index 2cdfcb6..34b1ac1 100644
--- a/src/views/inventoryManagement/stockManagement/Subtract.vue
+++ b/src/views/inventoryManagement/stockManagement/Subtract.vue
@@ -61,6 +61,16 @@
<el-input-number v-model="formState.qualitity" :step="1" :min="1" :max="maxQuality" style="width: 100%" />
</el-form-item>
+ <el-form-item label="鍑哄簱鎵规" prop="outboundBatches">
+ <el-input
+ v-model="formState.outboundBatches"
+ placeholder="鐣欑┖鑷姩鐢熸垚"
+ maxlength="100"
+ show-word-limit
+ />
+ <div class="form-tip">涓嶅~鍒欒嚜鍔ㄧ敓鎴愶紝鏍煎紡锛欳K骞存湀鏃�-搴忓彿</div>
+ </el-form-item>
+
<el-form-item label="澶囨敞" prop="remark">
<el-input v-model="formState.remark" type="textarea" />
</el-form-item>
@@ -134,6 +144,7 @@
model: "",
unit: "",
qualitity: 0,
+ outboundBatches: "",
remark: '',
});
@@ -157,6 +168,7 @@
productModelId: undefined,
productName: "",
productModelName: "",
+ outboundBatches: "",
description: '',
};
isShow.value = false;
@@ -216,4 +228,12 @@
handleSubmit,
isShow,
});
-</script>
\ No newline at end of file
+</script>
+
+<style scoped>
+.form-tip {
+ font-size: 12px;
+ color: #909399;
+ margin-top: 4px;
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3