项目开始时间 2023年10月01日
项目现状 目前(维护中)
一、总结:
1、期间遇到问题,自己网上查资料,最终解决;
去百度搜索的时候,很多情况搜不到的原因是自己没有用专业的语句,口语化的语句很少能搜到答案,有时候问一问别人或许不能直接解决问题,但是却给了我们另一个思路去搜索,这样说不定就能找到答案。
2、前一天晚上想好第二天要做的内容,划分好时间,提高开发效率。
3、拿到需求到代码实现经历了怎样的过程?
站在用户角度使用:
拿到ui设计稿,把所有的页面先点一点,大概了解有哪些功能?在这一步,我们可以参考其他成熟的系统,如果有类似功能的话,就去点一点,了解每个操作。知道新功能的基本流程是哪些。
站在技术角度:
思考自己上一步都做了些什么。每个功能的要点在哪里。实现的难点在哪里。可能会发生什么样的情况,类似于if...else方式的思考。这一步,我们作为技术,考虑的是如何实现。
4、项目描述:
5、项目技术:Vue3 + element-plus + vue-router + Pinia + axios + Echarts
6、项目职责:
使用 Pinia 实现数据统一管理;
使用 Vue3 的 vite 框架 、element-plus 实现快速布局;
使用 watchEffect 方法统一监听数据,从而更高效的解决数据变,页面变;
使用 Axios 网络请求库 ,调用后端接口获取数据 ,并将数据处理后传递给组件进行展示;
进行代码调试和单元测试 ,确保项目的稳定性和可靠性;
利用组件化思想 ,将页面封装成多个组件提高代码的复用性和可维护性;
性能优化,减少不必要的更新、使用异步组件加载、懒加载路由等,以提升页面加载速度和用户体验;
二、系统遇到的问题:
1、适配
什么是自适应布局?
自适应设计是能使网页自适应显示在不同大小终端设备上新网页设计方式及技术。简单的来说自适应就是让同一个页面自动适应不同大小的设备,从而解决为不同设备提供不同版本的页面问题。
解决方式:响应式布局,系统分为header上,menu左,main右,使用flex布局,并且右边main处不能出现滚动条,只可以table出现滚动条。
2、 element-plus的tree组件(一键选择)
需求:页面上展示所有的二/三级指标,要求点击按钮"一键选择二/三级指标",tree组件的复选框勾选上。
问题:使用tree组件自带方法setCheckedKeys,但是时好时坏,有些时候复选框勾不全,控制台会报错。
解决方式:使用tree组件自带方法default-checked-keys。点击按钮时请求拿到所有的二三级指标并循环,根据等级字段判断,把ID push到default-checked-keys变量中。
setCheckedKeys:设置目前选中的节点,使用此方法必须设置 node-key
属性
default-checked-keys:默认勾选的节点的 key 的数组
3.把后端返回的数据,转成tree数据
第一种方式:
后端返回的数据
转换成tree代码
javascript
export let transformData2 = (nodes) => {
const map = {};
let node;
let roots = [];
// 将所有节点转换成一个映射表
for (let i = 0; i < nodes.length; i++) {
map[nodes[i].jgbm || nodes[i].id] = nodes[i];
const hasChidren = nodes.some((item) => {
return item.fid || item.sjjgbm == nodes[i].jgbm || nodes[i].id;
});
if (hasChidren) {
nodes[i].children = [];
}
}
// 将所有子节点添加到其父节点的 children 属性中
for (let i = 0; i < nodes.length; i++) {
node = nodes[i];
if (
(node.fid || node.sjjgbm) != 0 &&
(node.fid || node.sjjgbm) !== null
) {
if (typeof map[node.fid || node.sjjgbm] !== "undefined") {
map[node.fid || node.sjjgbm].children.push(node);
} else {
roots.push(node);
}
} else {
roots.push(node);
}
}
return roots;
};
第二种方式:
后端返回的数据
转换成tree代码
javascript
export let transformData = (nodes) => {
const map1 = [];
// 第一级树
nodes.map((item, index) => {
map1[index] = {};
map1[index].id = item.flmc_parent_id;
map1[index].zbmc = item.flmc_parent;
map1[index].children = [];
});
// 去重
for (let i = 0; i < map1.length; i++) {
for (let j = i + 1; j < map1.length; j++) {
if (map1[i].id == map1[j].id) {
map1.splice(j, 1);
j--;
}
}
}
// 第二级树
map1.map((item, index) => {
nodes.map((v, i) => {
if (item.id == v.flmc_parent_id) {
map1[index].children[i] = {};
map1[index].children[i].id = v.flmc_id;
map1[index].children[i].zbmc = v.flmc;
map1[index].children[i].children = [];
}
});
});
// 去重
const newArr = map1.map((item) => {
return item.children.filter((v, i, arr) => {
return arr.indexOf(v) === i;
});
});
// 去重
newArr.map((item) => {
for (let i = 0; i < item.length; i++) {
for (let j = i + 1; j < item.length; j++) {
if (item[i].id == item[j].id) {
item.splice(j, 1);
j--;
}
}
}
});
// 第三级树(自己)
newArr.map((m, n) =>
m.map((item, index) => {
nodes.map((v, i) => {
if (item.id == v.flmc_id) {
item.children[i] = {
...v, // 展开运算符
};
}
});
})
);
// 去重
const dudududu = newArr.map((item) => {
return item.map((m) => {
return m.children.filter((v, i, arr) => {
return arr.indexOf(v) === i;
});
});
});
// 去重
dudududu.map((item) => {
item.map((m) => {
for (let i = 0; i < m.length; i++) {
for (let j = i + 1; j < m.length; j++) {
if (m[i].id == m[j].id) {
m.splice(j, 1);
j--;
}
}
}
});
});
// 把第三级树(自己)给到第二级树,第二级树给到第一级树
for (var i = 0; i < map1.length; i++) {
for (var m = 0; m < map1[i].children.length; m++) {
map1[i].children = newArr[i];
map1[i].children[m].children = dudududu[i][m];
}
}
return map1;
};
4、前端实现分页 (分享)
使用了v3的watchEffect
watchEffect也是监听数据,但是它会立即运行一个函数,而不是懒侦听。
如果需要页面加载完毕立即执行的话,还是用watchEffect
watchEffect 它与 watch 的区别主要有以下几点:
1.不需要手动传入依赖
2.每次初始化时会执行一次回调函数来自动获取依赖
3.无法获取到原值,只能得到变化后的值
4.watch
是惰性的,因此仅当依赖项更改时才会触发。watchEffect
在创建组件后立即运行,然后跟踪依赖关系。
javascript
const tableData = ref([]);
const currentTableData = ref([]); // table组件的data值
const current = ref(1); // 页码
const total = ref(1); // 总条数
watchEffect(() => {
current.value = 1;
total.value = tableData.value.length;
currentTableData.value = tableData.value.filter(
(item, index) => index < 30
);
});
// 第几页
let handleCurrentChange = (val) => {
current.value = val;
const index = 30 * (val - 1);
const nums = 30 * val;
const tables = [];
for (let i = index; i < nums; i++) {
if (tableData.value[i]) tables.push(tableData.value[i]);
}
currentTableData.value = tables;
};
// 模板渲染后,立即执行的函数
onMounted(() => {
// 页面刚进来时,请求的数据给到tableData.value
});
5、页面跳转后,点击返回,改为返回来源页。
javascript
// 页面相关参数
const searchParams = reactive({
ckzYear: "",
year: "2024",
SchemeName: "",
faName: "",
isJCindex: "",
options: [],
newArr: [],
homeList: [],
});
// 点击按钮跳转
let ckFun = async (item) => {
let _data = Object.assign(searchParams, {});
setstorage("nowPageData", JSON.stringify(_data)); //这两行代码用来往sessionStorage存数据
router.push("/StatisticalAnalysis-detail");
};
// 模板渲染后,立即执行的函数
onMounted(() => {
if (getstorage("nowPageData")) {
let _obj = JSON.parse(getstorage("nowPageData"));
Object.keys(searchParams).forEach((key) => {
searchParams[key] = _obj[key] || searchParams[key];
});
removelist("nowPageData");
}
});
6、计算指标分(综合计算)
需求:出院患者微创手术占比 = (日间手术台次数 / 同期出院患者择期手术总台次数) * 100%
思路:
1.拿到分子和分母的value
2.用字符串replace方法替换 str.replace(换下的值,换上的值)
3.使用eval。 (eval 是Javascript内置函数,用于计算字符串表达式的值。例如eval(2+3) 返回的是5。)
实现代码:
javascript
// 审核确定按钮
let util = () =>
{
// 计算公式编码
let jsgsbm = "({SJYLZL_GNDW_5_51 / SJYLZL_GNDW_5_52 * 100%})"
// 计算公式名称
let jsgsmc = "(5.1.出院患者微创手术台次数/5.2.同期出院患者手术台次数)*100%"
// 分子和分母信息
let fz_fm = [
{
bool: false,
cqz: "0",
faid: 1,
fz_or_fm: "fz",
khdx: "医院",
khnf: "2024",
value: "221",
zbdm: "SJYLZL_GNDW_5_51",
zbmc: "出院患者微创手术台次数"
}, {
bool: false,
cqz: "0",
faid: 1,
fz_or_fm: "fm",
khdx: "医院",
khnf: "2024",
value: "226",
zbdm: "SJYLZL_GNDW_5_52",
zbmc: "同期出院患者手术台次数"
}
]
let str = "";
for (var i = 0; i < jsgsbm.length; i++) {
if (
jsgsbm[i] == "{" ||
jsgsbm[i] == "}"
) {
continue;
} else if (jsgsbm[i] == "%") {
str += "";
} else {
str += jsgsbm[i];
}
}
for (var i = 0; i < fz_fm.length; i++) {
str = str.replace(
fz_fm[i].zbdm,
fz_fm[i].value == "/"
? "0"
: fz_fm[i].value
);
}
console.log(str);
return String((eval(str) / 1).toFixed(2))
}
console.log(util());