明树Git Lab

Commit 5d2eb68b authored by chenron's avatar chenron

提交

parent ad49af42
......@@ -6,6 +6,4 @@
<script setup></script>
<style scoped lang="less">
@import "./assets/font/font.less";
</style>
<style scoped lang="less"></style>
@font-face {
font-family: 'YouSheBiaoTiHei-Regular';
src: url('../font/字体/优设标题黑.ttf');
font-weight: normal;
font-style: normal;
}
\ No newline at end of file
@font-face {
font-family: 'YouSheBiaoTiHei';
src: url('../fonts/优设标题黑.ttf') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'YouSheBiaoTiYuan';
src: url('../fonts/优设标题圆.otf') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'DIN';
src: url('../fonts/0.otf') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Alibaba PuHuiTi';
src: url('../fonts/2.otf') format('woff');
font-weight: normal;
font-style: normal;
}
\ No newline at end of file
......@@ -5,22 +5,34 @@
<div class="stat-card-value">
<div class="card-value-top">
<div>
<p class="pro-title">11月完成</p>
<p class="pro-value">1.1亿元</p>
<p class="pro-title">{{ item.protitle }}</p>
<p class="pro-value">{{ item.proValue }}<span>亿元</span></p>
</div>
<div>
<p class="pro-title">第三季度完成</p>
<p class="pro-value">4.21亿元</p>
<p class="pro-title">{{ item.thirdTtile }}</p>
<p class="pro-value">{{ item.thirdValue }}<span>亿元</span></p>
</div>
</div>
<div class="card-value-bottom">
<div>
<p class="pro-title">2025年累计完成</p>
<p class="pro-value">112.1亿元</p>
<p class="pro-value">112.1<span>亿元</span></p>
</div>
<div class="value-bottom-right">
<p class="comparison">累计同期环比</p>
<p class="comparison-value">+1.2%</p>
<p
class="comparison-value"
:class="{ 'down-trend': item.title === '运营成本' }"
>
{{ item.compareValue }}
<el-icon
class="trend-icon"
:class="{ 'down-trend': item.title === '运营成本' }"
>
<Bottom v-if="item.title === '运营成本'" />
<Top v-else />
</el-icon>
</p>
</div>
</div>
</div>
......@@ -30,6 +42,7 @@
<script setup>
import { onMounted, reactive, watch, ref } from "vue";
import { Top, Bottom } from "@element-plus/icons-vue";
const props = defineProps({
numberList: {
type: Array,
......@@ -48,7 +61,7 @@ const props = defineProps({
.vw(border-radius, 5);
.vw(padding, 10);
.vw(margin,10);
background-image: url("../assets/images/complete.png");
background-image: url("@/assets/images/complete.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
......@@ -66,22 +79,46 @@ const props = defineProps({
.vh(height,10);
}
.pro-title {
.font(12);
.font(10);
margin-bottom: -12px;
}
.pro-value {
.font(16);
.font(18);
font-family: "DIN";
color: @Color;
font-weight: 100;
span {
.font(10);
font-weight: 400;
}
}
}
.value-bottom-right {
.vh(margin-top,10);
.font(10);
.comparison {
.font(10);
font-family: "Alibaba PuHuiTi";
}
.comparison-value {
color: green;
color: #32d25a;
display: flex;
align-items: center;
gap: 2px;
&.down-trend {
color: #ff4757;
}
.trend-icon {
font-size: 12px;
color: #32d25a;
&.down-trend {
color: #ff4757;
}
}
}
}
.value-bottom-right {
.vh(margin-top,13);
}
}
}
}
......
......@@ -6,7 +6,7 @@
<script setup>
import AMapLoader from "@amap/amap-jsapi-loader";
import { onMounted } from "vue";
import gansuLine from "./gansu.json";
import gansuLine from "./newLine.json";
let map = null;
window._AMapSecurityConfig = {
securityJsCode: "75a8291a9e8f7c838fc5e4dd0d538f30",
......
<template>
<div class="stat-cards-container">
<div class="stat-card" v-for="(item, idx) in props.numberList" :key="idx">
<div class="stat-card__title">{{ item.title }}</div>
<div class="stat-card__value">{{ item.value }}</div>
<div class="stat-card-title">{{ item.title }}</div>
<div class="stat-card-value">{{ item.value }}</div>
<div class="amount">
<div>
<p class="amount-title">实际完成金额</p>
<p class="amount-value">2.87亿元</p>
<p class="amount-value">2.87<span>亿元</span></p>
</div>
<div>
<p class="amount-title">计划完成金额</p>
<p class="amount-value">4.21亿元</p>
<p class="amount-value">4.21<span>亿元</span></p>
</div>
</div>
</div>
......@@ -34,16 +34,18 @@ const props = defineProps({
.vw(border-radius, 5);
.vw(padding, 10);
.vw(margin,10);
background-image: url("../assets/images/total2.png");
background-image: url("@/assets/images/total2.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
.stat-card__title {
.stat-card-title {
.font(12);
font-weight: 700;
}
.stat-card__value {
.font(16);
.stat-card-value {
.font(20);
color: @Color;
font-family: "DIN";
}
.amount {
display: flex;
......@@ -53,8 +55,14 @@ const props = defineProps({
.vh(height, 5);
}
.amount-value {
.font(20);
.font(16);
color: @Color;
font-family: "DIN";
font-weight: 100;
span {
.font(10);
font-weight: 400;
}
}
}
}
......
<template>
<div class="stat-cards-container">
<div class="stat-card" v-for="(item, idx) in props.numberList" :key="idx">
<div class="stat-card__title">{{ item.title }}</div>
<div class="stat-card__value">{{ item.value }}</div>
<div class="common-container">
<div class="stat-cards-container">
<div class="stat-card" v-for="(item, idx) in props.numberList" :key="idx">
<div class="stat-card-title">{{ item.title }}</div>
<div class="stat-card-value" @click="handleDetail(item)">
{{ item.value }}
</div>
</div>
</div>
<!-- 详情信息 -->
<el-dialog
:title="dialogTitle + '详情'"
v-model="dialogVisible"
width="50%"
:before-close="handleClose"
>
<commonTable
:data="tableData"
:columns="tableColumns"
:total="tableTotal"
:current-page="currentPage"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-page-change="handleCurrentPageChange"
@row-click="handleRowClick"
>
<template #status="{ row }">
<el-tag :type="row.status === 'active' ? 'success' : 'danger'">
{{ row.status === "active" ? "正常" : "异常" }}
</el-tag>
</template>
</commonTable>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false"
>确 定</el-button
>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, reactive, watch, ref } from "vue";
import commonTable from "./common/commonTable.vue";
const props = defineProps({
numberList: {
type: Array,
default: () => [],
},
});
// 弹窗相关
const dialogVisible = ref(false);
const dialogTitle = ref("");
// 表格相关数据
const tableData = ref([]);
const tableColumns = ref([
{ prop: "id", label: "ID", autoWidth: true },
{ prop: "name", label: "名称", autoWidth: true },
{ prop: "type", label: "类型", autoWidth: true },
{ prop: "value", label: "数值", autoWidth: true },
{ prop: "date", label: "日期", autoWidth: true },
{ prop: "status", label: "状态", autoWidth: true, slot: "status" },
]);
// 分页相关
const currentPage = ref(1);
const pageSize = ref(10);
const tableTotal = ref(0);
const generateTableData = () => {
const data = [];
const types = ["类型A", "类型B", "类型C"];
const statuses = ["active", "inactive"];
for (let i = 1; i <= 10; i++) {
data.push({
id: i,
name: `项目${i}`,
type: types[Math.floor(Math.random() * types.length)],
value: Math.floor(Math.random() * 1000),
date: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000
).toLocaleDateString(),
status: statuses[Math.floor(Math.random() * statuses.length)],
});
}
tableTotal.value = data.length;
return data;
};
const handleDetail = (item) => {
dialogVisible.value = true;
dialogTitle.value = item.title;
tableData.value = generateTableData();
};
// 分页处理
const handleSizeChange = (val) => {
pageSize.value = val;
currentPage.value = 1;
loadTableData();
};
const handleCurrentPageChange = (val) => {
currentPage.value = val;
loadTableData();
};
const loadTableData = () => {
const allData = generateTableData();
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
tableData.value = allData.slice(start, end);
};
const handleRowClick = (row, column, event) => {
console.log("行点击:", row);
};
const handleClose = () => {
dialogVisible.value = false;
};
onMounted(() => {});
</script>
<style scoped lang="less">
.stat-cards-container {
.common-container {
display: flex;
.stat-card {
.vw(width, 170);
.vw(border-radius, 5);
.vw(padding, 10);
.vw(margin,10);
background-image: url("../assets/images/total2.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
.stat-card__title {
.font(12);
}
.stat-card__value {
.vh(margin-top, 10);
.font(20);
justify-content: center;
.stat-cards-container {
display: flex;
.stat-card {
.vw(width, 170);
.vw(border-radius, 5);
.vw(padding, 20);
// .vw(margin,10);
background-image: url("@/assets/images/total2.png");
background-repeat: no-repeat;
background-size: 90% 80%;
background-position: center;
.stat-card-title {
.font(12);
}
.stat-card-value {
// .vh(margin-top, 10);
.font(24);
cursor: pointer;
font-family: "DIN";
}
}
}
}
:deep(.el-dialog__title) {
font-weight: 900;
}
</style>
<template>
<div class="common-table">
<div class="table-header" v-if="showHeader">
<div class="header-left">
<h3 v-if="title">{{ title }}</h3>
</div>
<div class="header-right">
<slot name="header-actions"></slot>
</div>
</div>
<div class="table-container">
<el-table
:data="tableData"
:height="height"
:max-height="maxHeight"
:stripe="stripe"
:border="border"
:size="size"
:fit="fit"
:show-header="showTableHeader"
:highlight-current-row="highlightCurrentRow"
:row-key="rowKey"
:empty-text="emptyText"
:default-expand-all="defaultExpandAll"
:expand-row-keys="expandRowKeys"
:default-sort="defaultSort"
:tooltip-effect="tooltipEffect"
:show-summary="showSummary"
:sum-text="sumText"
:summary-method="summaryMethod"
:span-method="spanMethod"
:select-on-indeterminate="selectOnIndeterminate"
:indent="indent"
:lazy="lazy"
:load="load"
:tree-props="treeProps"
@select="handleSelect"
@select-all="handleSelectAll"
@selection-change="handleSelectionChange"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
@cell-click="handleCellClick"
@cell-dblclick="handleCellDblClick"
@row-click="handleRowClick"
@row-contextmenu="handleRowContextmenu"
@row-dblclick="handleRowDblClick"
@header-click="handleHeaderClick"
@header-contextmenu="handleHeaderContextmenu"
@sort-change="handleSortChange"
@filter-change="handleFilterChange"
@current-change="handleCurrentChange"
@header-dragend="handleHeaderDragend"
@expand-change="handleExpandChange"
>
<el-table-column
v-if="selection"
type="selection"
width="55"
:selectable="selectable"
:reserve-selection="reserveSelection"
/>
<el-table-column v-if="expand" type="expand" width="50">
<template #default="props">
<slot name="expand" :row="props.row" :index="props.$index"></slot>
</template>
</el-table-column>
<el-table-column
v-if="index"
type="index"
width="60"
:label="indexLabel"
:index="indexMethod"
/>
<template v-for="column in columns" :key="column.prop">
<el-table-column
:prop="column.prop"
:label="column.label"
:width="column.autoWidth ? undefined : column.width"
:min-width="column.minWidth || (column.autoWidth ? undefined : 120)"
:fixed="column.fixed"
:render-header="column.renderHeader"
:sortable="column.sortable"
:sort-method="column.sortMethod"
:sort-by="column.sortBy"
:sort-orders="column.sortOrders"
:resizable="column.resizable !== false"
:formatter="column.formatter"
:show-overflow-tooltip="column.showOverflowTooltip !== false"
:align="column.align || 'left'"
:header-align="column.headerAlign"
:class-name="column.className"
:label-class-name="column.labelClassName"
:filters="column.filters"
:filter-placement="column.filterPlacement"
:filter-multiple="column.filterMultiple"
:filter-method="column.filterMethod"
:filtered-value="column.filteredValue"
>
<template #default="scope" v-if="column.slot">
<slot
:name="column.slot"
:row="scope.row"
:column="column"
:index="scope.$index"
></slot>
</template>
</el-table-column>
</template>
</el-table>
</div>
<div class="table-footer" v-if="pagination">
<el-config-provider :locale="zhCn">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="pageSizes"
:total="total"
:layout="paginationLayout"
:background="paginationBackground"
:small="paginationSmall"
:disabled="paginationDisabled"
:hide-on-single-page="hideOnSinglePage"
@size-change="handleSizeChange"
@current-change="handleCurrentPageChange"
@prev-click="handlePrevClick"
@next-click="handleNextClick"
/>
</el-config-provider>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
// Props 定义
const props = defineProps({
// 表格数据
data: {
type: Array,
default: () => [],
},
// 表格列配置
columns: {
type: Array,
default: () => [],
},
// 表格标题
title: {
type: String,
default: "",
},
// 是否显示表格头部
showHeader: {
type: Boolean,
default: true,
},
// 是否显示表头
showTableHeader: {
type: Boolean,
default: true,
},
// 表格高度
height: [String, Number],
// 表格最大高度
maxHeight: [String, Number],
// 是否为斑马纹表格
stripe: {
type: Boolean,
default: false,
},
// 是否带有纵向边框
border: {
type: Boolean,
default: false,
},
// Table 的尺寸
size: {
type: String,
default: "default", // large, default, small
},
// 列的宽度是否自撑开
fit: {
type: Boolean,
default: true,
},
// 是否要高亮当前行
highlightCurrentRow: {
type: Boolean,
default: false,
},
// 行数据的 Key
rowKey: [String, Function],
// 空数据时显示的文本内容
emptyText: {
type: String,
default: "暂无数据",
},
// 是否默认展开所有行
defaultExpandAll: {
type: Boolean,
default: false,
},
// 可以通过该属性设置目前展开的行
expandRowKeys: Array,
// 默认的排序列的 prop 和顺序
defaultSort: Object,
// tooltip effect 属性
tooltipEffect: {
type: String,
default: "dark", // dark, light
},
// 是否在表尾显示合计行
showSummary: {
type: Boolean,
default: false,
},
// 合计行第一列的文本
sumText: {
type: String,
default: "合计",
},
// 自定义的合计计算方法
summaryMethod: Function,
// 合并行或列的计算方法
spanMethod: Function,
// 在多选表格中,当仅有部分行被选中时的点击行为
selectOnIndeterminate: {
type: Boolean,
default: true,
},
// 展示树形数据时,树节点的缩进
indent: {
type: Number,
default: 16,
},
// 是否懒加载子节点数据
lazy: {
type: Boolean,
default: false,
},
// 加载子节点数据的函数
load: Function,
// 渲染嵌套数据的配置选项
treeProps: {
type: Object,
default: () => ({
hasChildren: "hasChildren",
children: "children",
}),
},
// 是否显示多选框
selection: {
type: Boolean,
default: false,
},
// 仅对 type=selection 的列有效,类型为 Function
selectable: Function,
// 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据
reserveSelection: {
type: Boolean,
default: false,
},
// 是否显示展开按钮
expand: {
type: Boolean,
default: false,
},
// 是否显示索引列
index: {
type: Boolean,
default: false,
},
// 索引列的标题
indexLabel: {
type: String,
default: "#",
},
// 自定义索引的计算方法
indexMethod: Function,
// 是否显示分页
pagination: {
type: Boolean,
default: true,
},
// 当前页码
currentPage: {
type: Number,
default: 1,
},
// 每页显示条数
pageSize: {
type: Number,
default: 10,
},
// 每页显示个数选择器的选项设置
pageSizes: {
type: Array,
default: () => [10, 20, 50, 100],
},
// 总条数
total: {
type: Number,
default: 0,
},
// 组件布局,子组件名用逗号分隔
paginationLayout: {
type: String,
default: "total, sizes, prev, pager, next, jumper",
},
// 是否为分页按钮添加背景色
paginationBackground: {
type: Boolean,
default: true,
},
// 是否使用小型分页
paginationSmall: {
type: Boolean,
default: false,
},
// 是否禁用分页
paginationDisabled: {
type: Boolean,
default: false,
},
// 只有一页时是否隐藏
hideOnSinglePage: {
type: Boolean,
default: false,
},
});
// Emits 定义
const emit = defineEmits([
"select",
"select-all",
"selection-change",
"cell-mouse-enter",
"cell-mouse-leave",
"cell-click",
"cell-dblclick",
"row-click",
"row-contextmenu",
"row-dblclick",
"header-click",
"header-contextmenu",
"sort-change",
"filter-change",
"current-change",
"header-dragend",
"expand-change",
"size-change",
"current-page-change",
"prev-click",
"next-click",
]);
// 响应式数据
const currentPage = ref(props.currentPage);
const pageSize = ref(props.pageSize);
// 计算属性
const tableData = computed(() => {
if (!props.pagination) {
return props.data;
}
return props.data;
});
// 监听器
watch(
() => props.currentPage,
(newVal) => {
currentPage.value = newVal;
}
);
watch(
() => props.pageSize,
(newVal) => {
pageSize.value = newVal;
}
);
// 事件处理函数
const handleSelect = (selection, row) => {
emit("select", selection, row);
};
const handleSelectAll = (selection) => {
emit("select-all", selection);
};
const handleSelectionChange = (selection) => {
emit("selection-change", selection);
};
const handleCellMouseEnter = (row, column, cell, event) => {
emit("cell-mouse-enter", row, column, cell, event);
};
const handleCellMouseLeave = (row, column, cell, event) => {
emit("cell-mouse-leave", row, column, cell, event);
};
const handleCellClick = (row, column, cell, event) => {
emit("cell-click", row, column, cell, event);
};
const handleCellDblClick = (row, column, cell, event) => {
emit("cell-dblclick", row, column, cell, event);
};
const handleRowClick = (row, column, event) => {
emit("row-click", row, column, event);
};
const handleRowContextmenu = (row, column, event) => {
emit("row-contextmenu", row, column, event);
};
const handleRowDblClick = (row, column, event) => {
emit("row-dblclick", row, column, event);
};
const handleHeaderClick = (column, event) => {
emit("header-click", column, event);
};
const handleHeaderContextmenu = (column, event) => {
emit("header-contextmenu", column, event);
};
const handleSortChange = (sort) => {
emit("sort-change", sort);
};
const handleFilterChange = (filters) => {
emit("filter-change", filters);
};
const handleCurrentChange = (currentRow, oldCurrentRow) => {
emit("current-change", currentRow, oldCurrentRow);
};
const handleHeaderDragend = (newWidth, oldWidth, column, event) => {
emit("header-dragend", newWidth, oldWidth, column, event);
};
const handleExpandChange = (row, expandedRows) => {
emit("expand-change", row, expandedRows);
};
const handleSizeChange = (val) => {
pageSize.value = val;
emit("size-change", val);
};
const handleCurrentPageChange = (val) => {
currentPage.value = val;
emit("current-page-change", val);
};
const handlePrevClick = (val) => {
emit("prev-click", val);
};
const handleNextClick = (val) => {
emit("next-click", val);
};
</script>
<style scoped lang="less">
.common-table {
background: #fff;
border-radius: 8px;
padding: 20px;
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.header-left {
h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #333;
}
}
.header-right {
display: flex;
gap: 12px;
}
}
.table-container {
margin-bottom: 16px;
}
.table-footer {
display: flex;
justify-content: flex-end;
align-items: center;
.el-pagination {
margin-top: 16px;
}
}
}
// 响应式设计
@media (max-width: 768px) {
.common-table {
padding: 12px;
.table-header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.table-footer {
justify-content: center;
.el-pagination {
justify-content: center;
}
}
}
}
:deep(.el-table) {
th.el-table__cell {
// background-color: var(--el-table-header-bg-color);
// background-color: #e8ebf0;
background-color: #f2f6fd;
}
thead {
color: var(--el-table-header-text-color);
}
.el-table__cell {
padding: 0;
height: 46px;
line-height: 46px;
}
.el-table__body tr.hover-row > td.el-table__cell {
background-color: var(--el-table-row-hover-bg-color);
}
.el-pagination.is-background {
.el-pager li {
border: 1px solid #e7e9ee;
// background-color: #fff;
font-weight: 400;
// color: #333;
}
.el-pager li:not(.disabled).is-active {
// background: rgba(91, 183, 59, 0.1);
border: none;
// color: var(--el-color-primary);
font-weight: 400;
}
.btn-prev,
.btn-next {
background-color: var(--el-disabled-bg-color);
}
}
.el-table__fixed-right-patch {
top: 0;
border: 1px solid #fff;
}
th.el-table__cell {
border-right: 1px solid #ebeef5;
}
.el-table__fixed-right {
top: -1px;
}
.el-table--border,
.el-table--group {
border: 0px;
}
.el-table__cell {
border-right: 0;
}
&::before {
width: 0px;
}
&::after {
width: 0px;
}
.el-table__border-left-patch {
width: 0;
}
}
:deep(.el-table__header .cell) {
display: flex;
align-items: center;
}
:deep(.el-table) {
background-color: rgba(255, 255, 255, 0.5);
}
</style>
......@@ -3,9 +3,9 @@
<!-- 顶部Header -->
<el-header class="city-header">
<div class="header-left">
<span class="city-name"></span>
<img src="@/assets/images/logo.png" alt="" />
<!-- <span class="city-name">123123</span> -->
</div>
<!-- </div> -->
<div class="header-right">
<div>
<el-dropdown>
......@@ -22,7 +22,6 @@
</el-dropdown>
</div>
</div>
<!-- </div> -->
</el-header>
<el-container>
......@@ -88,7 +87,7 @@ const handleLogout = () => {
};
</script>
<style scoped>
<style scoped lang="less">
.smart-city-container {
height: 100vh;
overflow: hidden;
......@@ -104,13 +103,15 @@ const handleLogout = () => {
.city-header {
width: 100%;
height: 64px;
background: #3db8c5
url("https://apaas-static.oss-cn-beijing.aliyuncs.com/app/header_bg.jpg")
no-repeat fixed top left / 100% 64px;
background: #3db8c5 url("@/assets/images/header-bg.png") no-repeat fixed top
left / 100% 64px;
color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
.header-left {
padding-left: 25px;
}
}
.city-name {
......
......@@ -5,6 +5,7 @@ import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElIcons from '@element-plus/icons'
import router from './router'
import "./assets/fonts/font.less"; // 字体样式
const app = createApp(App)
for (const [key, component] of Object.entries(ElIcons)) {
......
......@@ -2,7 +2,7 @@
<div class="construct-container">
<div class="construct-left">
<div class="tag-image">
<img src="../assets/images/建设.png" />
<img src="@/assets/images/建设.png" />
</div>
<div class="construct-left-map">
<Map />
......@@ -42,8 +42,8 @@
</template>
<script setup>
import Map from "./Map.vue";
import CommonTotal from "./CommonTotal.vue";
import Map from "@/components/CommonMap.vue";
import CommonTotal from "@/components/CommonTotal.vue";
import { reactive, ref, onMounted, onUnmounted } from "vue";
import CircleProgress from "./CircleProgress.vue";
const projectList = reactive([
......@@ -89,18 +89,16 @@ const recycleList = reactive([
.vw(width, 180);
.vh(height, 30);
.vh(line-height, 30);
.vw(margin-left,10);
.font(14);
.vw(margin-left,20);
.font(20);
.vw(padding-left,23);
text-align: left;
color: #fff;
font-family: YouSheBiaoTiYuan;
background-image: url("../assets/images/tag.png");
background-image: url("@/assets/images/tag.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
font-weight: 700;
letter-spacing: 3px;
background-size: 100% 100%;
}
.progress {
......@@ -119,7 +117,7 @@ const recycleList = reactive([
align-items: center;
.vw(width,520);
.vh(height,85);
background-image: url("../assets/images/箭头.png");
background-image: url("@/assets/images/箭头.png");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
......
......@@ -2,7 +2,7 @@
<div class="construct-container">
<div class="construct-left">
<div class="tag-image">
<img src="../assets/images/建设.png" />
<img src="@/assets/images/建设.png" />
</div>
<div class="construct-left-map">
<Map />
......@@ -44,10 +44,10 @@
</template>
<script setup>
import Map from "./Map.vue";
import CommonTotal from "./CommonTotal.vue";
import CommonComplete from "./CommonComplete.vue";
import CommonPlant from "./CommonPlant.vue";
import Map from "@/components/CommonMap.vue";
import CommonTotal from "@/components/CommonTotal.vue";
import CommonComplete from "@/components/CommonComplete.vue";
import CommonPlant from "@/components/CommonPlant.vue";
import { reactive, ref, onMounted, onUnmounted } from "vue";
const projectList = reactive([
{ title: "总个数(个)", value: "59" },
......@@ -70,9 +70,30 @@ const operationList = reactive([
{ title: "利润总额完成进度", value: "68.1%" },
]);
const completeList = reactive([
{ title: "营业收入" },
{ title: "运营成本" },
{ title: "利润总额" },
{
title: "营业收入",
protitle: "11月完成",
proValue: "1.1",
thirdTtile: "第三季度完成",
thirdValue: "4.21",
compareValue: "+1.2%",
},
{
title: "运营成本",
protitle: "11月完成",
proValue: "47.98",
thirdTtile: "第三季度完成",
thirdValue: "5.75",
compareValue: "-0.5%",
},
{
title: "利润总额",
protitle: "11月完成",
proValue: "9.90",
thirdTtile: "第三季度完成",
thirdValue: "23.52",
compareValue: "+1.2%",
},
]);
</script>
......@@ -92,21 +113,19 @@ const completeList = reactive([
flex-direction: column;
justify-content: space-between;
.info-title {
.vw(width, 220);
.vw(width, 230);
.vh(height, 30);
.vh(line-height, 30);
.vw(margin-left,10);
.font(14);
.vw(padding-left,23);
.font(20);
.vw(padding-left,28);
text-align: left;
color: #fff;
font-family: YouSheBiaoTiYuan;
background-image: url("../assets/images/tag.png");
background-image: url("@/assets/images/tag.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
font-weight: 700;
letter-spacing: 3px;
background-size: 100% 100%;
}
}
......
<template>
<div class="stat-cards-container">
<div class="stat-card" v-for="(item, idx) in numberList" :key="idx">
<div class="stat-card__title">{{ item.title }}</div>
<div class="stat-card__value">{{ item.value }}</div>
</div>
</div>
<CommonTotal :numberList="initiationList" />
<Map />
</template>
<script setup>
import Map from "./Map.vue";
import CommonTotal from "@/components/CommonTotal.vue";
import Map from "@/components/CommonMap.vue";
import { onMounted, reactive, watch, ref } from "vue";
const props = defineProps({
......@@ -18,19 +14,17 @@ const props = defineProps({
default: "立项",
},
});
const numberList = ref([]);
const initiationList = ref([]);
// 根据currentName设置不同的数据
const setNumberList = (name) => {
if (name === "立项") {
numberList.value = [
initiationList.value = [
{ title: "总个数(个)", value: "59" },
{ title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" },
];
} else {
numberList.value = [
initiationList.value = [
{ title: "总个数(个)", value: "59" },
{ title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" },
......@@ -67,14 +61,14 @@ onMounted(() => {
.vw(padding, 10);
.vw(padding, 10);
.vw(margin,10);
background-image: url("../assets/images/total.png");
background-image: url("@/assets/images/total.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
.stat-card__title {
.stat-card-title {
.font(14);
}
.stat-card__value {
.stat-card-value {
.vh(margin-top, 20);
.font(20);
}
......
......@@ -73,14 +73,14 @@
</template>
<script setup>
import Construct from "@/components/Construct.vue";
import ProjectApproval from "@/components/ProjectApproval.vue";
import Operation from "@/components/Operation.vue";
import Construct from "../components/Construct.vue";
import ProjectApproval from "../components/ProjectApproval.vue";
import Operation from "../components/Operation.vue";
import { reactive, ref } from "vue";
const selectedLeftBtn = ref("equity");
const selectedRightBtn = ref("");
const selectedContentBtn = ref(1);
const selectedContentBtn = ref(3);
const selectedContentName = ref("立项");
const isFullscreen = ref(false);
const showPopup = ref(false);
......@@ -188,12 +188,13 @@ window.addEventListener("fullscreenchange", () => {
<style scoped lang="less">
.home-container {
background-image: url("../../assets/images/bg.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
// background-image: url("@/assets/images/bg.png");
// background-repeat: no-repeat;
// background-size: cover;
// background-position: center;
width: 100%;
height: 100%;
background-color: #04427e;
overflow: hidden;
color: #fff;
display: flex;
......@@ -204,38 +205,44 @@ window.addEventListener("fullscreenchange", () => {
.vh(height, 80);
text-align: center;
.title-section {
background-image: url("../../assets/images/header.png");
background-image: url("@/assets/images/header.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
font-weight: 700;
letter-spacing: 3px;
background-size: 170% 100%;
.vh(margin-top, -15);
.font(24);
font-family: HelveticaNeue;
.font(28);
letter-spacing: 1px;
font-weight: 400;
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
font-family: "YouSheBiaoTiHei";
width: 950px;
height: 74px;
display: flex;
justify-content: center;
align-items: center;
}
.header-left {
display: flex;
// justify-content: center;
align-items: center;
padding-left: 75px;
.vw( padding-left, 75);
.nav-btn {
.vh(line-height, 36);
.font(14);
cursor: pointer;
color: #fff;
background-repeat: no-repeat;
background-size: 100%, 100%;
background-size: 85%, 100%;
background-position: center;
font-family: YouSheBiaoTiYuan;
font-weight: 100;
&:not(.active) {
background-image: url("../../assets/images/left-btn.png");
background-image: url("@/assets/images/left-btn.png");
.vh(height,36);
.vw(width,208);
}
&.active {
background-image: url("../../assets/images/left-hlightBtn.png");
background-image: url("@/assets/images/left-hlightBtn.png");
.vh(height,36);
.vw(width,208);
}
......@@ -253,15 +260,15 @@ window.addEventListener("fullscreenchange", () => {
background-repeat: no-repeat;
background-size: cover;
background-position: center;
font-family: YouSheBiaoTiYuan;
&:not(.active) {
background-image: url("../../assets/images/right-btn.png");
background-image: url("@/assets/images/right-btn.png");
.vh(height,36);
.vw(width,208);
}
&.active {
background-image: url("../../assets/images/right-hight-btn.png");
background-image: url("@/assets/images/right-hight-btn.png");
.vh(height,36);
.vw(width,208);
}
......@@ -275,61 +282,79 @@ window.addEventListener("fullscreenchange", () => {
.content-btn {
.vh(margin-top,-10);
.vh(margin-bottom,20);
text-align: center;
display: flex;
justify-content: center;
align-items: center;
span {
.font(14);
.vw(width,110);
.vh(height,35);
display: inline-block;
width: 110px;
height: 35px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
flex-shrink: 0;
color: #fff; // 补充文字颜色(匹配蓝色按钮的白色文字)
}
// 立项按钮
:nth-child(1) {
background-repeat: no-repeat;
background-size: cover;
background-size: 80% 80%;
background-position: center;
.vw(width,105);
.vh(height,35);
.vh(line-height,35);
width: 115px;
height: 35px;
line-height: 35px;
text-align: center;
.vw(margin-right,-7);
background-image: url("../../assets/images/立项.png");
&.active {
background-image: url("../../assets/images/nav-hight-btn.png");
background-image: url("@/assets/images/立项.png");
}
&:not(.active) {
background-image: url("@/assets/images/initiation-default.png");
}
}
// 退出按钮
:nth-child(5) {
background-repeat: no-repeat;
background-size: cover;
background-size: 80% 80%;
background-position: center;
.vw(width,105);
.vh(height,35);
.vh(line-height,35);
width: 111px;
height: 35px;
line-height: 35px;
text-align: center;
background-image: url("../../assets/images/退出.png");
&.active {
background-image: url("../../assets/images/退出-active.png");
background-image: url("@/assets/images/退出-active.png");
}
&:not(.active) {
background-image: url("@/assets/images/退出.png");
}
}
// 中间按钮(建设、运营、转止)
:nth-child(2),
:nth-child(3),
:nth-child(4) {
background-repeat: no-repeat;
background-size: cover;
background-size: 80% 80%;
background-position: center;
.vw(width,108);
.vh(height,35);
.vh(line-height,35);
.vw(margin-right,-7);
width: 115px;
height: 35px;
line-height: 35px;
text-align: center;
background-image: url("../../assets/images/default-btn.png");
margin-left: -32px;
background-image: url("@/assets/images/default-btn.png");
&.active {
background-image: url("../../assets/images/nav-hight-btn.png");
background-image: url("@/assets/images/nav-hight-btn.png");
}
}
:nth-child(5) {
margin-left: -31px;
}
}
}
.bottom {
background-image: url("../../assets/images/bottom.png");
background-image: url("@/assets/images/bottom.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
......
<template>
<div class="login-container">
<div class="login-header">葛洲坝集团交通投资有限公司投资决策分析系统</div>
<div class="login-form-wrapper">
<h2 class="login-title">后台管理系统登录</h2>
<div class="company-logo"></div>
<el-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
label-width="80px"
label-width="70px"
class="login-form"
labelPosition="top"
>
<el-form-item label="用户名" prop="username">
<el-input
:prefix-icon="User"
v-model="loginForm.username"
placeholder="请输入用户名"
prefix-icon="el-icon-user"
......@@ -18,10 +21,11 @@
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
prefix-icon="Lock"
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
prefix-icon="el-icon-lock"
show-password
></el-input>
</el-form-item>
<el-form-item>
......@@ -39,23 +43,25 @@
</div>
</template>
<script setup lang="ts">
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
import type { FormInstance, FormRules } from "element-plus";
import { User, Lock } from "@element-plus/icons-vue";
const router = useRouter();
const loginFormRef = ref<FormInstance>();
const loginFormRef = ref();
const loading = ref(false);
const captchaSrc = ref("https://picsum.photos/120/40"); // 模拟验证码图片
// 登录表单数据
const loginForm = ref({
username: "",
username: "admin",
password: "",
captcha: "",
});
// 表单验证规则
const loginRules = ref<FormRules>({
const loginRules = ref({
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" },
......@@ -69,12 +75,8 @@ const loginRules = ref<FormRules>({
// 处理登录
const handleLogin = async () => {
try {
// 验证表单
await loginFormRef.value?.validate();
loading.value = true;
// 模拟登录请求
setTimeout(() => {
// 登录成功后设置token
const mockToken = "mock-jwt-token";
......@@ -92,51 +94,135 @@ const handleLogin = async () => {
<style scoped lang="less">
.login-container {
width: 100%;
height: 100vh;
background-image: url("@/assets/images/login.png");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
padding-top: 15px;
}
.login-header {
color: #fff;
font-size: 24px;
font-weight: bold;
padding-left: 20px;
margin: 0 20px;
border-radius: 5px;
display: flex;
justify-content: flex-end;
justify-content: left;
align-items: center;
height: 100vh;
// background-image: url('@/assets/images/background.png');
// background-size: cover;
// background-position: center;
// background-repeat: no-repeat;
// padding-right: 100px;
height: 50px;
background: linear-gradient(
to right,
rgba(0, 0, 0, 0.3),
rgba(0, 0, 0, 0.6),
rgba(0, 0, 0, 0.3)
);
}
.login-form-wrapper {
background-color: rgba(255, 255, 255, 0.9);
background-color: rgba(0, 0, 0, 0.8);
position: absolute;
top: 35%;
right: 6%;
top: 50%;
right: 1%;
transform: translateY(-50%);
padding: 48px;
padding: 15px;
box-sizing: border-box;
width: 460px;
height: 550px;
box-shadow: 0 0 16px #2e31361a;
width: 350px;
height: 350px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
border-radius: 8px;
:deep(.el-form-item) {
margin-bottom: 10px;
.el-form-item__content {
.el-input {
height: 48px;
border-radius: 4px;
.el-input__wrapper {
background: #f3f4f9;
background: rgba(255, 255, 255, 0.9);
}
.el-input__inner {
color: #333;
}
}
.el-form-item__label {
color: #fff;
}
}
}
:deep(.el-form-item__label) {
color: #fff;
}
}
.login-title {
text-align: center;
.company-logo {
width: 100%;
height: 30px;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 30px;
color: #304156;
background-image: url(/src/assets/images/logo.png);
background-size: 100% 80%;
background-position: center;
background-repeat: no-repeat;
}
.login-form {
.login-btn {
width: 100%;
height: 30px;
border-radius: 4px;
background-color: #1890ff;
margin-top: 18px;
}
.forgot-password {
color: #fff;
float: right;
margin-top: 10px;
}
}
.captcha-image {
position: absolute;
right: 0;
top: 0;
cursor: pointer;
img {
width: 120px;
height: 40px;
border-radius: 4px;
}
}
:deep(.el-form-item__content) {
position: relative;
&.el-form-item__content--with-captcha {
padding-right: 130px;
}
}
:deep(.el-input__inner) {
color: #333;
}
:deep(.el-input__prefix-inner) {
color: #909399;
}
.login-form {
:deep(.el-form-item) {
.el-form-item__content {
.el-input {
height: 30px;
}
}
}
:deep(.el-form-item--label-top) {
.el-form-item__label {
line-height: 15px;
}
}
}
</style>
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