Compare commits
12 Commits
6c7b99ec50
...
d503e64098
Author | SHA1 | Date | |
---|---|---|---|
d503e64098 | |||
e7b0b54f01 | |||
5d76471571 | |||
d03efb8d6f | |||
748464b44a | |||
4a0962b117 | |||
ff8dd472de | |||
b331d17032 | |||
39e239a886 | |||
76ae5832c0 | |||
3c9ed824d3 | |||
8ef37c5a96 |
@ -9,7 +9,7 @@ VITE_APP_BASE_API = 'http://192.168.110.119:8899'
|
||||
|
||||
# 无人机接口地址
|
||||
|
||||
VITE_APP_BASE_DRONE_API = 'http://192.168.110.8:9136'
|
||||
VITE_APP_BASE_DRONE_API = 'http://58.17.134.85:9512'
|
||||
|
||||
# 应用访问路径 例如使用前缀 /admin/
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
|
@ -14,7 +14,7 @@ VITE_APP_MONITOR_ADMIN = '/admin/applications'
|
||||
VITE_APP_SNAILJOB_ADMIN = '/snail-job'
|
||||
|
||||
# 生产环境
|
||||
VITE_APP_BASE_API = 'http://192.168.110.2:8899'
|
||||
VITE_APP_BASE_API = 'http://58.17.134.85:8899'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
|
@ -212,6 +212,9 @@
|
||||
<script type="text/javascript"
|
||||
src="http://58.17.134.85:7363/changxieoffice/web-apps/apps/api/documents/api.js"></script>
|
||||
<script src="/js/html-docx.js"></script>
|
||||
<script src="/webrtc/adapter-7.4.0.min.js"></script>
|
||||
<script src="/webrtc/jquery-1.12.2.min.js"></script>
|
||||
<script src="/webrtc/srs.sdk.js"></script>
|
||||
|
||||
<script src="./src/assets/sdk/YJEarth.min.js"></script>
|
||||
<script src="./src/utils/reconnecting-websocket.js"></script>
|
||||
|
BIN
public/webrtc/AdobeFlashPlayerInstall.swf
Normal file
BIN
public/webrtc/AdobeFlashPlayerInstall.swf
Normal file
Binary file not shown.
5551
public/webrtc/adapter-7.4.0.js
Normal file
5551
public/webrtc/adapter-7.4.0.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/webrtc/adapter-7.4.0.min.js
vendored
Normal file
1
public/webrtc/adapter-7.4.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
public/webrtc/bootstrap.min.js
vendored
Normal file
6
public/webrtc/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
public/webrtc/css/bootstrap.min.css
vendored
Normal file
9
public/webrtc/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
19
public/webrtc/dash-v4.5.1.all.min.js
vendored
Normal file
19
public/webrtc/dash-v4.5.1.all.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/webrtc/dash.all.min.js.map
Normal file
1
public/webrtc/dash.all.min.js.map
Normal file
File diff suppressed because one or more lines are too long
2
public/webrtc/hls-0.14.17.min.js
vendored
Normal file
2
public/webrtc/hls-0.14.17.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/webrtc/hls.min.js.map
Normal file
1
public/webrtc/hls.min.js.map
Normal file
File diff suppressed because one or more lines are too long
5
public/webrtc/jquery-1.12.2.min.js
vendored
Normal file
5
public/webrtc/jquery-1.12.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/webrtc/jquery-1.12.2.min.map
Normal file
1
public/webrtc/jquery-1.12.2.min.map
Normal file
File diff suppressed because one or more lines are too long
486
public/webrtc/json2.js
Normal file
486
public/webrtc/json2.js
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
json2.js
|
||||
2013-05-26
|
||||
|
||||
Public Domain.
|
||||
|
||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
|
||||
See http://www.JSON.org/js.html
|
||||
|
||||
|
||||
This code should be minified before deployment.
|
||||
See http://javascript.crockford.com/jsmin.html
|
||||
|
||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
||||
NOT CONTROL.
|
||||
|
||||
|
||||
This file creates a global JSON object containing two methods: stringify
|
||||
and parse.
|
||||
|
||||
JSON.stringify(value, replacer, space)
|
||||
value any JavaScript value, usually an object or array.
|
||||
|
||||
replacer an optional parameter that determines how object
|
||||
values are stringified for objects. It can be a
|
||||
function or an array of strings.
|
||||
|
||||
space an optional parameter that specifies the indentation
|
||||
of nested structures. If it is omitted, the text will
|
||||
be packed without extra whitespace. If it is a number,
|
||||
it will specify the number of spaces to indent at each
|
||||
level. If it is a string (such as '\t' or ' '),
|
||||
it contains the characters used to indent at each level.
|
||||
|
||||
This method produces a JSON text from a JavaScript value.
|
||||
|
||||
When an object value is found, if the object contains a toJSON
|
||||
method, its toJSON method will be called and the result will be
|
||||
stringified. A toJSON method does not serialize: it returns the
|
||||
value represented by the name/value pair that should be serialized,
|
||||
or undefined if nothing should be serialized. The toJSON method
|
||||
will be passed the key associated with the value, and this will be
|
||||
bound to the value
|
||||
|
||||
For example, this would serialize Dates as ISO strings.
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z';
|
||||
};
|
||||
|
||||
You can provide an optional replacer method. It will be passed the
|
||||
key and value of each member, with this bound to the containing
|
||||
object. The value that is returned from your method will be
|
||||
serialized. If your method returns undefined, then the member will
|
||||
be excluded from the serialization.
|
||||
|
||||
If the replacer parameter is an array of strings, then it will be
|
||||
used to select the members to be serialized. It filters the results
|
||||
such that only members with keys listed in the replacer array are
|
||||
stringified.
|
||||
|
||||
Values that do not have JSON representations, such as undefined or
|
||||
functions, will not be serialized. Such values in objects will be
|
||||
dropped; in arrays they will be replaced with null. You can use
|
||||
a replacer function to replace those with JSON values.
|
||||
JSON.stringify(undefined) returns undefined.
|
||||
|
||||
The optional space parameter produces a stringification of the
|
||||
value that is filled with line breaks and indentation to make it
|
||||
easier to read.
|
||||
|
||||
If the space parameter is a non-empty string, then that string will
|
||||
be used for indentation. If the space parameter is a number, then
|
||||
the indentation will be that many spaces.
|
||||
|
||||
Example:
|
||||
|
||||
text = JSON.stringify(['e', {pluribus: 'unum'}]);
|
||||
// text is '["e",{"pluribus":"unum"}]'
|
||||
|
||||
|
||||
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
|
||||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
||||
|
||||
text = JSON.stringify([new Date()], function (key, value) {
|
||||
return this[key] instanceof Date ?
|
||||
'Date(' + this[key] + ')' : value;
|
||||
});
|
||||
// text is '["Date(---current time---)"]'
|
||||
|
||||
|
||||
JSON.parse(text, reviver)
|
||||
This method parses a JSON text to produce an object or array.
|
||||
It can throw a SyntaxError exception.
|
||||
|
||||
The optional reviver parameter is a function that can filter and
|
||||
transform the results. It receives each of the keys and values,
|
||||
and its return value is used instead of the original value.
|
||||
If it returns what it received, then the structure is not modified.
|
||||
If it returns undefined then the member is deleted.
|
||||
|
||||
Example:
|
||||
|
||||
// Parse the text. Values that look like ISO date strings will
|
||||
// be converted to Date objects.
|
||||
|
||||
myData = JSON.parse(text, function (key, value) {
|
||||
var a;
|
||||
if (typeof value === 'string') {
|
||||
a =
|
||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
||||
if (a) {
|
||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
||||
+a[5], +a[6]));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
|
||||
var d;
|
||||
if (typeof value === 'string' &&
|
||||
value.slice(0, 5) === 'Date(' &&
|
||||
value.slice(-1) === ')') {
|
||||
d = new Date(value.slice(5, -1));
|
||||
if (d) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
|
||||
This is a reference implementation. You are free to copy, modify, or
|
||||
redistribute.
|
||||
*/
|
||||
|
||||
/*jslint evil: true, regexp: true */
|
||||
|
||||
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
||||
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
||||
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
||||
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
||||
test, toJSON, toString, valueOf
|
||||
*/
|
||||
|
||||
|
||||
// Create a JSON object only if one does not already exist. We create the
|
||||
// methods in a closure to avoid creating global variables.
|
||||
|
||||
if (typeof JSON !== 'object') {
|
||||
JSON = {};
|
||||
}
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
Date.prototype.toJSON = function () {
|
||||
|
||||
return isFinite(this.valueOf())
|
||||
? this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z'
|
||||
: null;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function () {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
|
||||
|
||||
function quote(string) {
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string'
|
||||
? c
|
||||
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' : '"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
function str(key, holder) {
|
||||
|
||||
// Produce a string from holder[key].
|
||||
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
|
||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
|
||||
// If we were called with a replacer function, then call the replacer to
|
||||
// obtain a replacement value.
|
||||
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
|
||||
// What happens next depends on the value's type.
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
|
||||
case 'number':
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
|
||||
// If the value is a boolean or null, convert it to a string. Note:
|
||||
// typeof null does not produce 'null'. The case is included here in
|
||||
// the remote chance that this gets fixed someday.
|
||||
|
||||
return String(value);
|
||||
|
||||
// If the type is 'object', we might be dealing with an object or an array or
|
||||
// null.
|
||||
|
||||
case 'object':
|
||||
|
||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||
// so watch out for that case.
|
||||
|
||||
if (!value) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
// Make an array to hold the partial results of stringifying this object value.
|
||||
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
// Is the value an array?
|
||||
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
|
||||
// The value is an array. Stringify every element. Use null as a placeholder
|
||||
// for non-JSON values.
|
||||
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
// Join all of the elements together, separated with commas, and wrap them in
|
||||
// brackets.
|
||||
|
||||
v = partial.length === 0
|
||||
? '[]'
|
||||
: gap
|
||||
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
|
||||
: '[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
// If the replacer is an array, use it to select the members to be stringified.
|
||||
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
if (typeof rep[i] === 'string') {
|
||||
k = rep[i];
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Otherwise, iterate through all of the keys in the object.
|
||||
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
|
||||
v = partial.length === 0
|
||||
? '{}'
|
||||
: gap
|
||||
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
|
||||
: '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
// space parameter, and returns a JSON text. The replacer can be a function
|
||||
// that can replace values, or an array of strings that will select the keys.
|
||||
// A default replacer method can be provided. Use of the space parameter can
|
||||
// produce text that is more easily readable.
|
||||
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
// If the space parameter is a number, make an indent string containing that
|
||||
// many spaces.
|
||||
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// If the space parameter is a string, it will be used as the indent string.
|
||||
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
// If there is a replacer, it must be a function or an array.
|
||||
// Otherwise, throw an error.
|
||||
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
// Make a fake root object containing our value under the key of ''.
|
||||
// Return the result of stringifying the value.
|
||||
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
// a JavaScript value if the text is a valid JSON text.
|
||||
|
||||
var j;
|
||||
|
||||
function walk(holder, key) {
|
||||
|
||||
// The walk method is used to recursively walk the resulting structure so
|
||||
// that modifications can be made.
|
||||
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
|
||||
// Parsing happens in four stages. In the first stage, we replace certain
|
||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
// In the second stage, we run the text against regular expressions that look
|
||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||
// because they can cause invocation, and '=' because it can cause mutation.
|
||||
// But just to be safe, we want to reject all unexpected forms.
|
||||
|
||||
// We split the second stage into 4 regexp operations in order to work around
|
||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||
// replace all simple value tokens with ']' characters. Third, we delete all
|
||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||
// we look to see that the remaining characters are only whitespace or ']' or
|
||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||
|
||||
if (/^[\],:{}\s]*$/
|
||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
|
||||
// In the third stage we use the eval function to compile the text into a
|
||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
|
||||
j = eval('(' + text + ')');
|
||||
|
||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a reviver function for possible transformation.
|
||||
|
||||
return typeof reviver === 'function'
|
||||
? walk({'': j}, '')
|
||||
: j;
|
||||
}
|
||||
|
||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
9
public/webrtc/mpegts-1.7.2.min.js
vendored
Normal file
9
public/webrtc/mpegts-1.7.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/webrtc/mpegts.js.map
Normal file
1
public/webrtc/mpegts.js.map
Normal file
File diff suppressed because one or more lines are too long
37
public/webrtc/srs.log.js
Normal file
37
public/webrtc/srs.log.js
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* log specified, there must be a log element as:
|
||||
<!-- for the log -->
|
||||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">标题:</span></strong>
|
||||
<span id="txt_log_msg">日志内容</span>
|
||||
</div>
|
||||
*/
|
||||
var srs_log_disabled = false;
|
||||
function info(desc) {
|
||||
if (srs_log_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
|
||||
$("#txt_log_title").text("Info:");
|
||||
$("#txt_log_msg").html(desc);
|
||||
}
|
||||
function warn(code, desc) {
|
||||
if (srs_log_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
|
||||
$("#txt_log_title").text("Warn:");
|
||||
$("#txt_log_msg").html("code: " + code + ", " + desc);
|
||||
}
|
||||
function error(code, desc) {
|
||||
if (srs_log_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
|
||||
$("#txt_log_title").text("Error:");
|
||||
$("#txt_log_msg").html("code: " + code + ", " + desc);
|
||||
}
|
182
public/webrtc/srs.page.js
Normal file
182
public/webrtc/srs.page.js
Normal file
@ -0,0 +1,182 @@
|
||||
|
||||
// to query the swf anti cache.
|
||||
function srs_get_version_code() { return "1.33"; }
|
||||
|
||||
/**
|
||||
* player specified size.
|
||||
*/
|
||||
function srs_get_player_modal() { return 740; }
|
||||
function srs_get_player_width() { return srs_get_player_modal() - 30; }
|
||||
function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }
|
||||
|
||||
/**
|
||||
* update the navigator, add same query string.
|
||||
*/
|
||||
function update_nav() {
|
||||
$("#nav_srs_player").attr("href", "srs_player.html" + window.location.search);
|
||||
$("#nav_rtc_player").attr("href", "rtc_player.html" + window.location.search);
|
||||
$("#nav_rtc_publisher").attr("href", "rtc_publisher.html" + window.location.search);
|
||||
$("#nav_whip").attr("href", "whip.html" + window.location.search);
|
||||
$("#nav_whep").attr("href", "whep.html" + window.location.search);
|
||||
$("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
|
||||
$("#nav_srs_chat").attr("href", "srs_chat.html" + window.location.search);
|
||||
$("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search);
|
||||
$("#nav_vlc").attr("href", "vlc.html" + window.location.search);
|
||||
}
|
||||
|
||||
// Special extra params, such as auth_key.
|
||||
function user_extra_params(query, params, rtc) {
|
||||
var queries = params || [];
|
||||
|
||||
for (var key in query.user_query) {
|
||||
if (key === 'app' || key === 'autostart' || key === 'dir'
|
||||
|| key === 'filename' || key === 'host' || key === 'hostname'
|
||||
|| key === 'http_port' || key === 'pathname' || key === 'port'
|
||||
|| key === 'server' || key === 'stream' || key === 'buffer'
|
||||
|| key === 'schema' || key === 'vhost' || key === 'api'
|
||||
|| key === 'path'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query[key]) {
|
||||
queries.push(key + '=' + query[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
function is_default_port(schema, port) {
|
||||
return (schema === 'http' && port === 80)
|
||||
|| (schema === 'https' && port === 443)
|
||||
|| (schema === 'webrtc' && port === 1985)
|
||||
|| (schema === 'rtmp' && port === 1935);
|
||||
}
|
||||
|
||||
/**
|
||||
@param server the ip of server. default to window.location.hostname
|
||||
@param vhost the vhost of HTTP-FLV. default to window.location.hostname
|
||||
@param port the port of HTTP-FLV. default to 1935
|
||||
@param app the app of HTTP-FLV. default to live.
|
||||
@param stream the stream of HTTP-FLV. default to livestream.flv
|
||||
*/
|
||||
function build_default_flv_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var schema = (!query.schema)? "http":query.schema;
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var port = (!query.port)? (schema==="http"? 8080:1935) : Number(query.port);
|
||||
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
|
||||
var app = (!query.app)? "live":query.app;
|
||||
var stream = (!query.stream)? "livestream.flv":query.stream;
|
||||
|
||||
var queries = [];
|
||||
if (server !== vhost && vhost !== "__defaultVhost__") {
|
||||
queries.push("vhost=" + vhost);
|
||||
}
|
||||
queries = user_extra_params(query, queries);
|
||||
|
||||
var uri = schema + "://" + server;
|
||||
if (!is_default_port(schema, port)) {
|
||||
uri += ":" + port;
|
||||
}
|
||||
uri += "/" + app + "/" + stream + "?" + queries.join('&');
|
||||
while (uri.indexOf("?") === uri.length - 1) {
|
||||
uri = uri.slice(0, uri.length - 1);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
function build_default_rtc_url(query) {
|
||||
// The format for query string to overwrite configs of server.
|
||||
console.log('?eip=x.x.x.x to overwrite candidate. 覆盖服务器candidate(外网IP)配置');
|
||||
console.log('?api=x to overwrite WebRTC API(1985).');
|
||||
console.log('?schema=http|https to overwrite WebRTC API protocol.');
|
||||
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
|
||||
var app = (!query.app)? "live":query.app;
|
||||
var stream = (!query.stream)? "livestream":query.stream;
|
||||
var api = query.api? ':'+query.api : '';
|
||||
|
||||
var queries = [];
|
||||
if (server !== vhost && vhost !== "__defaultVhost__") {
|
||||
queries.push("vhost=" + vhost);
|
||||
}
|
||||
if (query.schema && window.location.protocol !== query.schema + ':') {
|
||||
queries.push('schema=' + query.schema);
|
||||
}
|
||||
queries = user_extra_params(query, queries, true);
|
||||
|
||||
var uri = "webrtc://" + server + api + "/" + app + "/" + stream + "?" + queries.join('&');
|
||||
while (uri.lastIndexOf("?") === uri.length - 1) {
|
||||
uri = uri.slice(0, uri.length - 1);
|
||||
}
|
||||
|
||||
return uri;
|
||||
};
|
||||
|
||||
function build_default_whip_whep_url(query, apiPath) {
|
||||
// The format for query string to overwrite configs of server.
|
||||
console.log('?eip=x.x.x.x to overwrite candidate. 覆盖服务器candidate(外网IP)配置');
|
||||
console.log('?api=x to overwrite WebRTC API(1985).');
|
||||
console.log('?schema=http|https to overwrite WebRTC API protocol.');
|
||||
console.log(`?path=xxx to overwrite default ${apiPath}`);
|
||||
|
||||
var server = (!query.server)? window.location.hostname:query.server;
|
||||
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
|
||||
var app = (!query.app)? "live":query.app;
|
||||
var stream = (!query.stream)? "livestream":query.stream;
|
||||
var api = ':' + (query.api || (window.location.protocol === 'http:' ? '1985' : '1990'));
|
||||
const realApiPath = query.path || apiPath;
|
||||
|
||||
var queries = [];
|
||||
if (server !== vhost && vhost !== "__defaultVhost__") {
|
||||
queries.push("vhost=" + vhost);
|
||||
}
|
||||
if (query.schema && window.location.protocol !== query.schema + ':') {
|
||||
queries.push('schema=' + query.schema);
|
||||
}
|
||||
queries = user_extra_params(query, queries, true);
|
||||
|
||||
var uri = window.location.protocol + "//" + server + api + realApiPath + "?app=" + app + "&stream=" + stream + "&" + queries.join('&');
|
||||
while (uri.lastIndexOf("?") === uri.length - 1) {
|
||||
uri = uri.slice(0, uri.length - 1);
|
||||
}
|
||||
while (uri.lastIndexOf("&") === uri.length - 1) {
|
||||
uri = uri.slice(0, uri.length - 1);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize the page.
|
||||
* @param flv_url the div id contains the flv stream url to play
|
||||
* @param hls_url the div id contains the hls stream url to play
|
||||
* @param modal_player the div id contains the modal player
|
||||
*/
|
||||
function srs_init_flv(flv_url, modal_player) {
|
||||
update_nav();
|
||||
if (flv_url) {
|
||||
$(flv_url).val(build_default_flv_url());
|
||||
}
|
||||
if (modal_player) {
|
||||
$(modal_player).width(srs_get_player_modal() + "px");
|
||||
$(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px");
|
||||
}
|
||||
}
|
||||
function srs_init_rtc(id, query) {
|
||||
update_nav();
|
||||
$(id).val(build_default_rtc_url(query));
|
||||
}
|
||||
function srs_init_whip(id, query) {
|
||||
update_nav();
|
||||
$(id).val(build_default_whip_whep_url(query, '/rtc/v1/whip/'));
|
||||
}
|
||||
function srs_init_whep(id, query) {
|
||||
update_nav();
|
||||
$(id).val(build_default_whip_whep_url(query, '/rtc/v1/whep/'));
|
||||
}
|
382
public/webrtc/srs.player.js
Normal file
382
public/webrtc/srs.player.js
Normal file
@ -0,0 +1,382 @@
|
||||
/**
|
||||
* the SrsPlayer object.
|
||||
* @param container the html container id.
|
||||
* @param width a float value specifies the width of player.
|
||||
* @param height a float value specifies the height of player.
|
||||
* @param private_object [optional] an object that used as private object,
|
||||
* for example, the logic chat object which owner this player.
|
||||
* Usage:
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.player.js"></script>
|
||||
<div id="player"></div>
|
||||
var p = new SrsPlayer("player", 640, 480);
|
||||
p.set_srs_player_url("srs_player.swf?v=1.0.0");
|
||||
p.on_player_ready = function() {
|
||||
p.set_bt(0.8);
|
||||
p.set_mbt(1.2);
|
||||
p.play("rtmp://ossrs.net/live/livestream");
|
||||
};
|
||||
p.on_player_metadata = function(metadata) {
|
||||
console.log(metadata);
|
||||
console.log(p.dump_log());
|
||||
};
|
||||
p.start();
|
||||
*/
|
||||
function SrsPlayer(container, width, height, private_object) {
|
||||
if (!SrsPlayer.__id) {
|
||||
SrsPlayer.__id = 100;
|
||||
}
|
||||
if (!SrsPlayer.__players) {
|
||||
SrsPlayer.__players = [];
|
||||
}
|
||||
|
||||
SrsPlayer.__players.push(this);
|
||||
|
||||
this.private_object = private_object;
|
||||
this.container = container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.id = SrsPlayer.__id++;
|
||||
this.stream_url = null;
|
||||
this.buffer_time = 0.3; // default to 0.3
|
||||
this.max_buffer_time = this.buffer_time * 3; // default to 3 x bufferTime.
|
||||
this.volume = 1.0; // default to 100%
|
||||
this.callbackObj = null;
|
||||
this.srs_player_url = "srs_player/release/srs_player.swf?_version="+srs_get_version_code();
|
||||
|
||||
// callback set the following values.
|
||||
this.meatadata = {}; // for on_player_metadata
|
||||
this.time = 0; // current stream time.
|
||||
this.buffer_length = 0; // current stream buffer length.
|
||||
this.kbps = 0; // current stream bitrate(video+audio) in kbps.
|
||||
this.fps = 0; // current stream video fps.
|
||||
this.rtime = 0; // flash relative time in ms.
|
||||
|
||||
this.__fluency = {
|
||||
total_empty_count: 0,
|
||||
total_empty_time: 0,
|
||||
current_empty_time: 0
|
||||
};
|
||||
this.__fluency.on_stream_empty = function(time) {
|
||||
this.total_empty_count++;
|
||||
this.current_empty_time = time;
|
||||
};
|
||||
this.__fluency.on_stream_full = function(time) {
|
||||
if (this.current_empty_time > 0) {
|
||||
this.total_empty_time += time - this.current_empty_time;
|
||||
this.current_empty_time = 0;
|
||||
}
|
||||
};
|
||||
this.__fluency.calc = function(time) {
|
||||
var den = this.total_empty_count * 4 + this.total_empty_time * 2 + time;
|
||||
if (den > 0) {
|
||||
return time * 100 / den;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* user can set some callback, then start the player.
|
||||
* @param url the default url.
|
||||
* callbacks:
|
||||
* on_player_ready():int, when srs player ready, user can play().
|
||||
* on_player_metadata(metadata:Object):int, when srs player get metadata.
|
||||
* methods:
|
||||
* set_bt(t:Number):void, set the buffer time in seconds.
|
||||
* set_mbt(t:Number):void, set the max buffer time in seconds.
|
||||
* dump_log():String, get all logs of player.
|
||||
*/
|
||||
SrsPlayer.prototype.start = function(url) {
|
||||
if (url) {
|
||||
this.stream_url = url;
|
||||
}
|
||||
|
||||
// embed the flash.
|
||||
var flashvars = {};
|
||||
flashvars.id = this.id;
|
||||
flashvars.on_player_ready = "__srs_on_player_ready";
|
||||
flashvars.on_player_metadata = "__srs_on_player_metadata";
|
||||
flashvars.on_player_timer = "__srs_on_player_timer";
|
||||
flashvars.on_player_empty = "__srs_on_player_empty";
|
||||
flashvars.on_player_full = "__srs_on_player_full";
|
||||
flashvars.on_player_status = "__srs_on_player_status";
|
||||
|
||||
var params = {};
|
||||
params.wmode = "opaque";
|
||||
params.allowFullScreen = "true";
|
||||
params.allowScriptAccess = "always";
|
||||
|
||||
var attributes = {};
|
||||
|
||||
var self = this;
|
||||
|
||||
swfobject.embedSWF(
|
||||
this.srs_player_url,
|
||||
this.container,
|
||||
this.width, this.height,
|
||||
"11.1.0", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
if (!callbackObj.success) {
|
||||
console.error('Initialize player failed:'); console.error(callbackObj);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* play the stream.
|
||||
* @param stream_url the url of stream, rtmp or http.
|
||||
* @param volume the volume, 0 is mute, 1 is 100%, 2 is 200%.
|
||||
*/
|
||||
SrsPlayer.prototype.play = function(url, volume) {
|
||||
this.stop();
|
||||
SrsPlayer.__players.push(this);
|
||||
|
||||
if (url) {
|
||||
this.stream_url = url;
|
||||
}
|
||||
|
||||
// volume maybe 0, so never use if(volume) to check its value.
|
||||
if (volume != null && volume != undefined) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time, this.max_buffer_time, this.volume);
|
||||
}
|
||||
/**
|
||||
* stop play stream.
|
||||
*/
|
||||
SrsPlayer.prototype.stop = function() {
|
||||
this.callbackObj.ref.__stop();
|
||||
}
|
||||
/**
|
||||
* pause the play.
|
||||
*/
|
||||
SrsPlayer.prototype.pause = function() {
|
||||
this.callbackObj.ref.__pause();
|
||||
}
|
||||
/**
|
||||
* resume the play.
|
||||
*/
|
||||
SrsPlayer.prototype.resume = function() {
|
||||
this.callbackObj.ref.__resume();
|
||||
}
|
||||
/**
|
||||
* get the stream fluency, where 100 is 100%.
|
||||
*/
|
||||
SrsPlayer.prototype.fluency = function() {
|
||||
return this.__fluency.calc(this.rtime);
|
||||
}
|
||||
/**
|
||||
* get the stream empty count.
|
||||
*/
|
||||
SrsPlayer.prototype.empty_count = function() {
|
||||
return this.__fluency.total_empty_count;
|
||||
}
|
||||
/**
|
||||
* get all log data.
|
||||
*/
|
||||
SrsPlayer.prototype.dump_log = function() {
|
||||
return this.callbackObj.ref.__dump_log();
|
||||
}
|
||||
/**
|
||||
* to set the DAR, for example, DAR=16:9 where num=16,den=9.
|
||||
* @param num, for example, 16.
|
||||
* use metadata width if 0.
|
||||
* use user specified width if -1.
|
||||
* @param den, for example, 9.
|
||||
* use metadata height if 0.
|
||||
* use user specified height if -1.
|
||||
*/
|
||||
SrsPlayer.prototype.set_dar = function(num, den) {
|
||||
this.callbackObj.ref.__set_dar(num, den);
|
||||
}
|
||||
/**
|
||||
* set the fullscreen size data.
|
||||
* @refer the refer fullscreen mode. it can be:
|
||||
* video: use video orignal size.
|
||||
* screen: use screen size to rescale video.
|
||||
* @param percent, the rescale percent, where
|
||||
* 100 means 100%.
|
||||
*/
|
||||
SrsPlayer.prototype.set_fs = function(refer, percent) {
|
||||
this.callbackObj.ref.__set_fs(refer, percent);
|
||||
}
|
||||
/**
|
||||
* set the stream buffer time in seconds.
|
||||
* @buffer_time the buffer time in seconds.
|
||||
*/
|
||||
SrsPlayer.prototype.set_bt = function(buffer_time) {
|
||||
if (this.buffer_time == buffer_time) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer_time = buffer_time;
|
||||
this.callbackObj.ref.__set_bt(buffer_time);
|
||||
|
||||
// reset the max buffer time to 3 x buffer_time.
|
||||
this.set_mbt(buffer_time * 3);
|
||||
}
|
||||
/**
|
||||
* set the stream max buffer time in seconds.
|
||||
* @param max_buffer_time the max buffer time in seconds.
|
||||
* @remark this is the key feature for realtime communication by flash.
|
||||
*/
|
||||
SrsPlayer.prototype.set_mbt = function(max_buffer_time) {
|
||||
// we must atleast set the max buffer time to 0.6s.
|
||||
max_buffer_time = Math.max(0.6, max_buffer_time);
|
||||
// max buffer time always greater than buffer time.
|
||||
max_buffer_time = Math.max(this.buffer_time, max_buffer_time);
|
||||
|
||||
if (parseInt(this.max_buffer_time * 10) == parseInt(max_buffer_time * 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.max_buffer_time = max_buffer_time;
|
||||
this.callbackObj.ref.__set_mbt(max_buffer_time);
|
||||
}
|
||||
/**
|
||||
* set the srs_player.swf url
|
||||
* @param url, srs_player.swf's url.
|
||||
* @param params, object.
|
||||
*/
|
||||
SrsPlayer.prototype.set_srs_player_url = function(url, params) {
|
||||
var query_array = [],
|
||||
query_string = "",
|
||||
p;
|
||||
params = params || {};
|
||||
params._version = srs_get_version_code();
|
||||
for (p in params) {
|
||||
if (params.hasOwnProperty(p)) {
|
||||
query_array.push(p + "=" + encodeURIComponent(params[p]));
|
||||
}
|
||||
}
|
||||
query_string = query_array.join("&");
|
||||
this.srs_player_url = url + "?" + query_string;
|
||||
}
|
||||
/**
|
||||
* the callback when player is ready.
|
||||
*/
|
||||
SrsPlayer.prototype.on_player_ready = function() {
|
||||
}
|
||||
/**
|
||||
* the callback when player got metadata.
|
||||
* @param metadata the metadata which player got.
|
||||
*/
|
||||
SrsPlayer.prototype.on_player_metadata = function(metadata) {
|
||||
// ignore.
|
||||
}
|
||||
/**
|
||||
* the callback when player timer event.
|
||||
* @param time current stream time.
|
||||
* @param buffer_length current buffer length.
|
||||
* @param kbps current video plus audio bitrate in kbps.
|
||||
* @param fps current video fps.
|
||||
* @param rtime current relative time by flash.util.getTimer().
|
||||
*/
|
||||
SrsPlayer.prototype.on_player_timer = function(time, buffer_length, kbps, fps, rtime) {
|
||||
// ignore.
|
||||
}
|
||||
/**
|
||||
* the callback when player got NetStream.Buffer.Empty
|
||||
* @param time current relative time by flash.util.getTimer().
|
||||
*/
|
||||
SrsPlayer.prototype.on_player_empty = function(time) {
|
||||
// ignore.
|
||||
}
|
||||
/**
|
||||
* the callback when player got NetStream.Buffer.Full
|
||||
* @param time current relative time by flash.util.getTimer().
|
||||
*/
|
||||
SrsPlayer.prototype.on_player_full = function(time) {
|
||||
// ignore.
|
||||
}
|
||||
/**
|
||||
* the callback when player status change.
|
||||
* @param code the status code, "init", "connected", "play", "closed", "rejected", "failed".
|
||||
* init => connected/rejected/failed
|
||||
* connected => play/rejected => closed
|
||||
* @param desc the description for the status.
|
||||
*/
|
||||
SrsPlayer.prototype.on_player_status = function(code, desc) {
|
||||
// ignore.
|
||||
}
|
||||
|
||||
/**
|
||||
* helpers.
|
||||
*/
|
||||
function __srs_find_player(id) {
|
||||
for (var i = 0; i < SrsPlayer.__players.length; i++) {
|
||||
var player = SrsPlayer.__players[i];
|
||||
|
||||
if (player.id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
throw new Error("player not found. id=" + id);
|
||||
}
|
||||
function __srs_on_player_ready(id) {
|
||||
var player = __srs_find_player(id);
|
||||
player.on_player_ready();
|
||||
}
|
||||
function __srs_on_player_metadata(id, metadata) {
|
||||
var player = __srs_find_player(id);
|
||||
|
||||
// user may override the on_player_metadata,
|
||||
// so set the data before invoke it.
|
||||
player.metadata = metadata;
|
||||
|
||||
player.on_player_metadata(metadata);
|
||||
}
|
||||
function __srs_on_player_timer(id, time, buffer_length, kbps, fps, rtime) {
|
||||
var player = __srs_find_player(id);
|
||||
|
||||
buffer_length = Math.max(0, buffer_length);
|
||||
buffer_length = Math.min(player.buffer_time, buffer_length);
|
||||
|
||||
time = Math.max(0, time);
|
||||
|
||||
// user may override the on_player_timer,
|
||||
// so set the data before invoke it.
|
||||
player.time = time;
|
||||
player.buffer_length = buffer_length;
|
||||
player.kbps = kbps;
|
||||
player.fps = fps;
|
||||
player.rtime = rtime;
|
||||
|
||||
player.on_player_timer(time, buffer_length, kbps, fps, rtime);
|
||||
}
|
||||
function __srs_on_player_empty(id, time) {
|
||||
var player = __srs_find_player(id);
|
||||
player.__fluency.on_stream_empty(time);
|
||||
player.on_player_empty(time);
|
||||
}
|
||||
function __srs_on_player_full(id, time) {
|
||||
var player = __srs_find_player(id);
|
||||
player.__fluency.on_stream_full(time);
|
||||
player.on_player_full(time);
|
||||
}
|
||||
function __srs_on_player_status(id, code, desc) {
|
||||
var player = __srs_find_player(id);
|
||||
player.on_player_status(code, desc);
|
||||
|
||||
if (code != "closed") {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < SrsPlayer.__players.length; i++) {
|
||||
var player = SrsPlayer.__players[i];
|
||||
|
||||
if (player.id != this.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SrsPlayer.__players.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
173
public/webrtc/srs.publisher.js
Normal file
173
public/webrtc/srs.publisher.js
Normal file
@ -0,0 +1,173 @@
|
||||
/**
|
||||
* the SrsPublisher object.
|
||||
* @param container the html container id.
|
||||
* @param width a float value specifies the width of publisher.
|
||||
* @param height a float value specifies the height of publisher.
|
||||
* @param private_object [optional] an object that used as private object,
|
||||
* for example, the logic chat object which owner this publisher.
|
||||
*/
|
||||
function SrsPublisher(container, width, height, private_object) {
|
||||
if (!SrsPublisher.__id) {
|
||||
SrsPublisher.__id = 100;
|
||||
}
|
||||
if (!SrsPublisher.__publishers) {
|
||||
SrsPublisher.__publishers = [];
|
||||
}
|
||||
|
||||
SrsPublisher.__publishers.push(this);
|
||||
|
||||
this.private_object = private_object;
|
||||
this.container = container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.id = SrsPublisher.__id++;
|
||||
this.callbackObj = null;
|
||||
|
||||
// set the values when publish.
|
||||
this.url = null;
|
||||
this.vcodec = {};
|
||||
this.acodec = {};
|
||||
|
||||
// callback set the following values.
|
||||
this.cameras = [];
|
||||
this.microphones = [];
|
||||
this.code = 0;
|
||||
|
||||
// error code defines.
|
||||
this.errors = {
|
||||
"100": "无法获取指定的摄像头。", //error_camera_get
|
||||
"101": "无法获取指定的麦克风。", //error_microphone_get
|
||||
"102": "摄像头为禁用状态,推流时请允许flash访问摄像头。", //error_camera_muted
|
||||
"103": "服务器关闭了连接。", //error_connection_closed
|
||||
"104": "服务器连接失败。", //error_connection_failed
|
||||
"199": "未知错误。"
|
||||
};
|
||||
}
|
||||
/**
|
||||
* user can set some callback, then start the publisher.
|
||||
* callbacks:
|
||||
* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish.
|
||||
* on_publisher_error(code, desc):int, when srs publisher error, callback this method.
|
||||
* on_publisher_warn(code, desc):int, when srs publisher warn, callback this method.
|
||||
*/
|
||||
SrsPublisher.prototype.start = function() {
|
||||
// embed the flash.
|
||||
var flashvars = {};
|
||||
flashvars.id = this.id;
|
||||
flashvars.width = this.width;
|
||||
flashvars.height = this.height;
|
||||
flashvars.on_publisher_ready = "__srs_on_publisher_ready";
|
||||
flashvars.on_publisher_error = "__srs_on_publisher_error";
|
||||
flashvars.on_publisher_warn = "__srs_on_publisher_warn";
|
||||
|
||||
var params = {};
|
||||
params.wmode = "opaque";
|
||||
params.allowFullScreen = "true";
|
||||
params.allowScriptAccess = "always";
|
||||
|
||||
var attributes = {};
|
||||
|
||||
var self = this;
|
||||
|
||||
swfobject.embedSWF(
|
||||
"srs_publisher/release/srs_publisher.swf?_version="+srs_get_version_code(),
|
||||
this.container,
|
||||
this.width, this.height,
|
||||
"11.1.0", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* publish stream to server.
|
||||
* @param url a string indicates the rtmp url to publish.
|
||||
* @param vcodec an object contains the video codec info.
|
||||
* @param acodec an object contains the audio codec info.
|
||||
*/
|
||||
SrsPublisher.prototype.publish = function(url, vcodec, acodec) {
|
||||
this.stop();
|
||||
SrsPublisher.__publishers.push(this);
|
||||
|
||||
if (url) {
|
||||
this.url = url;
|
||||
}
|
||||
if (vcodec) {
|
||||
this.vcodec = vcodec;
|
||||
}
|
||||
if (acodec) {
|
||||
this.acodec = acodec;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__publish(this.url, this.width, this.height, this.vcodec, this.acodec);
|
||||
}
|
||||
SrsPublisher.prototype.stop = function() {
|
||||
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
|
||||
var player = SrsPublisher.__publishers[i];
|
||||
|
||||
if (player.id != this.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SrsPublisher.__publishers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__stop();
|
||||
}
|
||||
/**
|
||||
* when publisher ready.
|
||||
* @param cameras a string array contains the names of cameras.
|
||||
* @param microphones a string array contains the names of microphones.
|
||||
*/
|
||||
SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) {
|
||||
}
|
||||
/**
|
||||
* when publisher error.
|
||||
* @code the error code.
|
||||
* @desc the error desc message.
|
||||
*/
|
||||
SrsPublisher.prototype.on_publisher_error = function(code, desc) {
|
||||
throw new Error("publisher error. code=" + code + ", desc=" + desc);
|
||||
}
|
||||
SrsPublisher.prototype.on_publisher_warn = function(code, desc) {
|
||||
throw new Error("publisher warn. code=" + code + ", desc=" + desc);
|
||||
}
|
||||
function __srs_find_publisher(id) {
|
||||
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
|
||||
var publisher = SrsPublisher.__publishers[i];
|
||||
|
||||
if (publisher.id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return publisher;
|
||||
}
|
||||
|
||||
throw new Error("publisher not found. id=" + id);
|
||||
}
|
||||
function __srs_on_publisher_ready(id, cameras, microphones) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.cameras = cameras;
|
||||
publisher.microphones = microphones;
|
||||
|
||||
publisher.on_publisher_ready(cameras, microphones);
|
||||
}
|
||||
function __srs_on_publisher_error(id, code) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.code = code;
|
||||
|
||||
publisher.on_publisher_error(code, publisher.errors[""+code]);
|
||||
}
|
||||
function __srs_on_publisher_warn(id, code) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.code = code;
|
||||
|
||||
publisher.on_publisher_warn(code, publisher.errors[""+code]);
|
||||
}
|
681
public/webrtc/srs.sdk.js
Normal file
681
public/webrtc/srs.sdk.js
Normal file
@ -0,0 +1,681 @@
|
||||
|
||||
//
|
||||
// Copyright (c) 2013-2021 Winlin
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
function SrsError(name, message) {
|
||||
this.name = name;
|
||||
this.message = message;
|
||||
this.stack = (new Error()).stack;
|
||||
}
|
||||
SrsError.prototype = Object.create(Error.prototype);
|
||||
SrsError.prototype.constructor = SrsError;
|
||||
|
||||
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||
// Async-awat-prmise based SRS RTC Publisher.
|
||||
function SrsRtcPublisherAsync() {
|
||||
var self = {};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
|
||||
self.constraints = {
|
||||
audio: true,
|
||||
video: {
|
||||
width: {ideal: 320, max: 576}
|
||||
}
|
||||
};
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
// or specifies the API port:
|
||||
// webrtc://r.ossrs.net:11985/live/livestream
|
||||
// or autostart the publish:
|
||||
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||
// or change the app from live to myapp:
|
||||
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||
// or change the stream from livestream to mystream:
|
||||
// webrtc://r.ossrs.net:11985/live/mystream
|
||||
// or set the api server to myapi.domain.com:
|
||||
// webrtc://myapi.domain.com/live/livestream
|
||||
// or set the candidate(eip) of answer:
|
||||
// webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
|
||||
// or force to access https API:
|
||||
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||
// or use plaintext, without SRTP:
|
||||
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||
// or any other information, will pass-by in the query:
|
||||
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||
self.publish = async function (url) {
|
||||
var conf = self.__internal.prepareUrl(url);
|
||||
self.pc.addTransceiver("audio", {direction: "sendonly"});
|
||||
self.pc.addTransceiver("video", {direction: "sendonly"});
|
||||
//self.pc.addTransceiver("video", {direction: "sendonly"});
|
||||
//self.pc.addTransceiver("audio", {direction: "sendonly"});
|
||||
|
||||
if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
|
||||
throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
|
||||
}
|
||||
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
|
||||
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
stream.getTracks().forEach(function (track) {
|
||||
self.pc.addTrack(track);
|
||||
|
||||
// Notify about local track when stream is ok.
|
||||
self.ontrack && self.ontrack({track: track});
|
||||
});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
var session = await new Promise(function (resolve, reject) {
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var data = {
|
||||
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
|
||||
clientip: null, sdp: offer.sdp
|
||||
};
|
||||
console.log("Generated offer: ", data);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
console.log("Got answer: ", data);
|
||||
return data.code ? reject(xhr) : resolve(data);
|
||||
}
|
||||
xhr.open('POST', conf.apiUrl, true);
|
||||
xhr.setRequestHeader('Content-type', 'application/json');
|
||||
xhr.send(JSON.stringify(data));
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||
);
|
||||
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
// Close the publisher.
|
||||
self.close = function () {
|
||||
self.pc && self.pc.close();
|
||||
self.pc = null;
|
||||
};
|
||||
|
||||
// The callback when got local stream.
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
self.ontrack = function (event) {
|
||||
// Add track to stream of SDK.
|
||||
self.stream.addTrack(event.track);
|
||||
};
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
defaultPath: '/rtc/v1/publish/',
|
||||
prepareUrl: function (webrtcUrl) {
|
||||
var urlObject = self.__internal.parse(webrtcUrl);
|
||||
|
||||
// If user specifies the schema, use it as API schema.
|
||||
var schema = urlObject.user_query.schema;
|
||||
schema = schema ? schema + ':' : window.location.protocol;
|
||||
|
||||
var port = urlObject.port || 1985;
|
||||
if (schema === 'https:') {
|
||||
port = urlObject.port || 443;
|
||||
}
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||
api += '/';
|
||||
}
|
||||
|
||||
var apiUrl = schema + '//' + urlObject.server + ':' + port + api;
|
||||
for (var key in urlObject.user_query) {
|
||||
if (key !== 'api' && key !== 'play') {
|
||||
apiUrl += '&' + key + '=' + urlObject.user_query[key];
|
||||
}
|
||||
}
|
||||
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
|
||||
apiUrl = apiUrl.replace(api + '&', api + '?');
|
||||
|
||||
var streamUrl = urlObject.url;
|
||||
|
||||
return {
|
||||
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
|
||||
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
|
||||
};
|
||||
},
|
||||
parse: function (url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
|
||||
var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.slice(app.indexOf("?"));
|
||||
app = app.slice(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.slice(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (url.indexOf("://") > 0) {
|
||||
schema = url.slice(0, url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
// Finger out by webrtc url, if contains http or https port, to overwrite default 1985.
|
||||
if (schema === 'webrtc' && url.indexOf(`webrtc://${a.host}:`) === 0) {
|
||||
port = (url.indexOf(`webrtc://${a.host}:80`) === 0) ? 80 : 443;
|
||||
}
|
||||
|
||||
// Guess by schema.
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
self.__internal.fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
fill_query: function (query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
|
||||
// To keep api consistent between player and publisher.
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
// @see https://webrtc.org/getting-started/media-devices
|
||||
self.stream = new MediaStream();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||
// Async-await-promise based SRS RTC Player.
|
||||
function SrsRtcPlayerAsync() {
|
||||
var self = {};
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// webrtc://r.ossrs.net/live/livestream
|
||||
// or specifies the API port:
|
||||
// webrtc://r.ossrs.net:11985/live/livestream
|
||||
// webrtc://r.ossrs.net:80/live/livestream
|
||||
// or autostart the play:
|
||||
// webrtc://r.ossrs.net/live/livestream?autostart=true
|
||||
// or change the app from live to myapp:
|
||||
// webrtc://r.ossrs.net:11985/myapp/livestream
|
||||
// or change the stream from livestream to mystream:
|
||||
// webrtc://r.ossrs.net:11985/live/mystream
|
||||
// or set the api server to myapi.domain.com:
|
||||
// webrtc://myapi.domain.com/live/livestream
|
||||
// or set the candidate(eip) of answer:
|
||||
// webrtc://r.ossrs.net/live/livestream?candidate=39.107.238.185
|
||||
// or force to access https API:
|
||||
// webrtc://r.ossrs.net/live/livestream?schema=https
|
||||
// or use plaintext, without SRTP:
|
||||
// webrtc://r.ossrs.net/live/livestream?encrypt=false
|
||||
// or any other information, will pass-by in the query:
|
||||
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
|
||||
// webrtc://r.ossrs.net/live/livestream?token=xxx
|
||||
self.play = async function(url) {
|
||||
var conf = self.__internal.prepareUrl(url);
|
||||
self.pc.addTransceiver("audio", {direction: "recvonly"});
|
||||
self.pc.addTransceiver("video", {direction: "recvonly"});
|
||||
//self.pc.addTransceiver("video", {direction: "recvonly"});
|
||||
//self.pc.addTransceiver("audio", {direction: "recvonly"});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
var session = await new Promise(function(resolve, reject) {
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var data = {
|
||||
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
|
||||
clientip: null, sdp: offer.sdp
|
||||
};
|
||||
console.log("Generated offer: ", data);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
console.log("Got answer: ", data);
|
||||
return data.code ? reject(xhr) : resolve(data);
|
||||
}
|
||||
xhr.open('POST', conf.apiUrl, true);
|
||||
xhr.setRequestHeader('Content-type', 'application/json');
|
||||
xhr.send(JSON.stringify(data));
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
|
||||
);
|
||||
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
// Close the player.
|
||||
self.close = function() {
|
||||
self.pc && self.pc.close();
|
||||
self.pc = null;
|
||||
};
|
||||
|
||||
// The callback when got remote track.
|
||||
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
|
||||
self.ontrack = function (event) {
|
||||
// https://webrtc.org/getting-started/remote-streams
|
||||
self.stream.addTrack(event.track);
|
||||
};
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
defaultPath: '/rtc/v1/play/',
|
||||
prepareUrl: function (webrtcUrl) {
|
||||
var urlObject = self.__internal.parse(webrtcUrl);
|
||||
|
||||
// If user specifies the schema, use it as API schema.
|
||||
var schema = urlObject.user_query.schema;
|
||||
schema = schema ? schema + ':' : window.location.protocol;
|
||||
|
||||
var port = urlObject.port || 1985;
|
||||
if (schema === 'https:') {
|
||||
port = urlObject.port || 443;
|
||||
}
|
||||
|
||||
// @see https://github.com/rtcdn/rtcdn-draft
|
||||
var api = urlObject.user_query.play || self.__internal.defaultPath;
|
||||
if (api.lastIndexOf('/') !== api.length - 1) {
|
||||
api += '/';
|
||||
}
|
||||
|
||||
var apiUrl = schema + '//' + urlObject.server + ':' + port + api;
|
||||
for (var key in urlObject.user_query) {
|
||||
if (key !== 'api' && key !== 'play') {
|
||||
apiUrl += '&' + key + '=' + urlObject.user_query[key];
|
||||
}
|
||||
}
|
||||
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
|
||||
apiUrl = apiUrl.replace(api + '&', api + '?');
|
||||
|
||||
var streamUrl = urlObject.url;
|
||||
|
||||
return {
|
||||
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
|
||||
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
|
||||
};
|
||||
},
|
||||
parse: function (url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
|
||||
var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.slice(app.indexOf("?"));
|
||||
app = app.slice(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.slice(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (url.indexOf("://") > 0) {
|
||||
schema = url.slice(0, url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
// Finger out by webrtc url, if contains http or https port, to overwrite default 1985.
|
||||
if (schema === 'webrtc' && url.indexOf(`webrtc://${a.host}:`) === 0) {
|
||||
port = (url.indexOf(`webrtc://${a.host}:80`) === 0) ? 80 : 443;
|
||||
}
|
||||
|
||||
// Guess by schema.
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
self.__internal.fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
fill_query: function (query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
|
||||
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
|
||||
self.stream = new MediaStream();
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
|
||||
self.pc.ontrack = function(event) {
|
||||
if (self.ontrack) {
|
||||
self.ontrack(event);
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
|
||||
// Async-awat-prmise based SRS RTC Publisher by WHIP.
|
||||
function SrsRtcWhipWhepAsync() {
|
||||
var self = {};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
|
||||
self.constraints = {
|
||||
audio: true,
|
||||
video: {
|
||||
width: {ideal: 320, max: 576}
|
||||
}
|
||||
};
|
||||
|
||||
// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
|
||||
// @url The WebRTC url to publish with, for example:
|
||||
// http://localhost:1985/rtc/v1/whip/?app=live&stream=livestream
|
||||
self.publish = async function (url) {
|
||||
if (url.indexOf('/whip/') === -1) throw new Error(`invalid WHIP url ${url}`);
|
||||
|
||||
self.pc.addTransceiver("audio", {direction: "sendonly"});
|
||||
self.pc.addTransceiver("video", {direction: "sendonly"});
|
||||
|
||||
if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
|
||||
throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
|
||||
}
|
||||
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
|
||||
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
stream.getTracks().forEach(function (track) {
|
||||
self.pc.addTrack(track);
|
||||
|
||||
// Notify about local track when stream is ok.
|
||||
self.ontrack && self.ontrack({track: track});
|
||||
});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
const answer = await new Promise(function (resolve, reject) {
|
||||
console.log("Generated offer: ", offer);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
|
||||
const data = xhr.responseText;
|
||||
console.log("Got answer: ", data);
|
||||
return data.code ? reject(xhr) : resolve(data);
|
||||
}
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader('Content-type', 'application/sdp');
|
||||
xhr.send(offer.sdp);
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: answer})
|
||||
);
|
||||
|
||||
return self.__internal.parseId(url, offer.sdp, answer);
|
||||
};
|
||||
|
||||
// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
|
||||
// @url The WebRTC url to play with, for example:
|
||||
// http://localhost:1985/rtc/v1/whep/?app=live&stream=livestream
|
||||
self.play = async function(url) {
|
||||
if (url.indexOf('/whip-play/') === -1 && url.indexOf('/whep/') === -1) throw new Error(`invalid WHEP url ${url}`);
|
||||
|
||||
self.pc.addTransceiver("audio", {direction: "recvonly"});
|
||||
self.pc.addTransceiver("video", {direction: "recvonly"});
|
||||
|
||||
var offer = await self.pc.createOffer();
|
||||
await self.pc.setLocalDescription(offer);
|
||||
const answer = await new Promise(function(resolve, reject) {
|
||||
console.log("Generated offer: ", offer);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
|
||||
const data = xhr.responseText;
|
||||
console.log("Got answer: ", data);
|
||||
return data.code ? reject(xhr) : resolve(data);
|
||||
}
|
||||
xhr.open('POST', url, true);
|
||||
xhr.setRequestHeader('Content-type', 'application/sdp');
|
||||
xhr.send(offer.sdp);
|
||||
});
|
||||
await self.pc.setRemoteDescription(
|
||||
new RTCSessionDescription({type: 'answer', sdp: answer})
|
||||
);
|
||||
|
||||
return self.__internal.parseId(url, offer.sdp, answer);
|
||||
};
|
||||
|
||||
// Close the publisher.
|
||||
self.close = function () {
|
||||
self.pc && self.pc.close();
|
||||
self.pc = null;
|
||||
};
|
||||
|
||||
// The callback when got local stream.
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
self.ontrack = function (event) {
|
||||
// Add track to stream of SDK.
|
||||
self.stream.addTrack(event.track);
|
||||
};
|
||||
|
||||
self.pc = new RTCPeerConnection(null);
|
||||
|
||||
// To keep api consistent between player and publisher.
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
|
||||
// @see https://webrtc.org/getting-started/media-devices
|
||||
self.stream = new MediaStream();
|
||||
|
||||
// Internal APIs.
|
||||
self.__internal = {
|
||||
parseId: (url, offer, answer) => {
|
||||
let sessionid = offer.substr(offer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
|
||||
sessionid = sessionid.substr(0, sessionid.indexOf('\n') - 1) + ':';
|
||||
sessionid += answer.substr(answer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
|
||||
sessionid = sessionid.substr(0, sessionid.indexOf('\n'));
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
return {
|
||||
sessionid: sessionid, // Should be ice-ufrag of answer:offer.
|
||||
simulator: a.protocol + '//' + a.host + '/rtc/v1/nack/',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
|
||||
self.pc.ontrack = function(event) {
|
||||
if (self.ontrack) {
|
||||
self.ontrack(event);
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
|
||||
function SrsRtcFormatSenders(senders, kind) {
|
||||
var codecs = [];
|
||||
senders.forEach(function (sender) {
|
||||
var params = sender.getParameters();
|
||||
params && params.codecs && params.codecs.forEach(function(c) {
|
||||
if (kind && sender.track.kind !== kind) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var s = '';
|
||||
|
||||
s += c.mimeType.replace('audio/', '').replace('video/', '');
|
||||
s += ', ' + c.clockRate + 'HZ';
|
||||
if (sender.track.kind === "audio") {
|
||||
s += ', channels: ' + c.channels;
|
||||
}
|
||||
s += ', pt: ' + c.payloadType;
|
||||
|
||||
codecs.push(s);
|
||||
});
|
||||
});
|
||||
return codecs.join(", ");
|
||||
}
|
||||
|
8
public/webrtc/srs.utility.js
Normal file
8
public/webrtc/srs.utility.js
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* parse the rtmp url,
|
||||
* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
|
||||
* @return object {server, port, vhost, app, stream}
|
||||
*/
|
||||
function srs_parse_rtmp_url(rtmp_url) {
|
||||
return parse_rtmp_url(rtmp_url);
|
||||
}
|
4
public/webrtc/swfobject.js
Normal file
4
public/webrtc/swfobject.js
Normal file
File diff suppressed because one or more lines are too long
686
public/webrtc/winlin.utility.js
Normal file
686
public/webrtc/winlin.utility.js
Normal file
@ -0,0 +1,686 @@
|
||||
// winlin.utility.js
|
||||
|
||||
/**
|
||||
* common utilities
|
||||
* depends: jquery1.10
|
||||
* https://gitee.com/winlinvip/codes/rpn0c2ewbomj81augzk4y59
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* v 1.0.23
|
||||
*/
|
||||
|
||||
/**
|
||||
* padding the output.
|
||||
* padding(3, 5, '0') is 00003
|
||||
* padding(3, 5, 'x') is xxxx3
|
||||
* @see http://blog.csdn.net/win_lin/article/details/12065413
|
||||
*/
|
||||
function padding(number, length, prefix) {
|
||||
if(String(number).length >= length){
|
||||
return String(number);
|
||||
}
|
||||
return padding(prefix+number, length, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* extends system array, to remove all specified elem.
|
||||
* @param arr the array to remove elem from.
|
||||
* @param elem the elem to remove.
|
||||
* @remark all elem will be removed.
|
||||
* for example,
|
||||
* arr = [10, 15, 20, 30, 20, 40]
|
||||
* system_array_remove(arr, 10) // arr=[15, 20, 30, 20, 40]
|
||||
* system_array_remove(arr, 20) // arr=[15, 30, 40]
|
||||
*/
|
||||
function system_array_remove(arr, elem) {
|
||||
if (!arr) {
|
||||
return;
|
||||
}
|
||||
|
||||
var removed = true;
|
||||
var i = 0;
|
||||
while (removed) {
|
||||
removed = false;
|
||||
for (; i < arr.length; i++) {
|
||||
if (elem == arr[i]) {
|
||||
arr.splice(i, 1);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* whether the array contains specified element.
|
||||
* @param arr the array to find.
|
||||
* @param elem_or_function the element value or compare function.
|
||||
* @returns true contains elem; otherwise false.
|
||||
* for example,
|
||||
* arr = [10, 15, 20, 30, 20, 40]
|
||||
* system_array_contains(arr, 10) // true
|
||||
* system_array_contains(arr, 11) // false
|
||||
* system_array_contains(arr, function(elem){return elem == 30;}); // true
|
||||
* system_array_contains(arr, function(elem){return elem == 60;}); // false
|
||||
*/
|
||||
function system_array_contains(arr, elem_or_function) {
|
||||
return system_array_get(arr, elem_or_function) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the specified element from array
|
||||
* @param arr the array to find.
|
||||
* @param elem_or_function the element value or compare function.
|
||||
* @returns the matched elem; otherwise null.
|
||||
* for example,
|
||||
* arr = [10, 15, 20, 30, 20, 40]
|
||||
* system_array_get(arr, 10) // 10
|
||||
* system_array_get(arr, 11) // null
|
||||
* system_array_get(arr, function(elem){return elem == 30;}); // 30
|
||||
* system_array_get(arr, function(elem){return elem == 60;}); // null
|
||||
*/
|
||||
function system_array_get(arr, elem_or_function) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (typeof elem_or_function == "function") {
|
||||
if (elem_or_function(arr[i])) {
|
||||
return arr[i];
|
||||
}
|
||||
} else {
|
||||
if (elem_or_function == arr[i]) {
|
||||
return arr[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* to iterate on array.
|
||||
* @param arr the array to iterate on.
|
||||
* @param pfn the function to apply on it. return false to break loop.
|
||||
* for example,
|
||||
* arr = [10, 15, 20, 30, 20, 40]
|
||||
* system_array_foreach(arr, function(elem, index){
|
||||
* console.log('index=' + index + ',elem=' + elem);
|
||||
* });
|
||||
* @return true when iterate all elems.
|
||||
*/
|
||||
function system_array_foreach(arr, pfn) {
|
||||
if (!pfn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (!pfn(arr[i], i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* whether the str starts with flag.
|
||||
*/
|
||||
function system_string_startswith(str, flag) {
|
||||
if (typeof flag == "object" && flag.constructor == Array) {
|
||||
for (var i = 0; i < flag.length; i++) {
|
||||
if (system_string_startswith(str, flag[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str && flag && str.length >= flag.length && str.indexOf(flag) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* whether the str ends with flag.
|
||||
*/
|
||||
function system_string_endswith(str, flag) {
|
||||
if (typeof flag == "object" && flag.constructor == Array) {
|
||||
for (var i = 0; i < flag.length; i++) {
|
||||
if (system_string_endswith(str, flag[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str && flag && str.length >= flag.length && str.indexOf(flag) == str.length - flag.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* trim the start and end of flag in str.
|
||||
* @param flag a string to trim.
|
||||
*/
|
||||
function system_string_trim(str, flag) {
|
||||
if (!flag || !flag.length || typeof flag != "string") {
|
||||
return str;
|
||||
}
|
||||
|
||||
while (system_string_startswith(str, flag)) {
|
||||
str = str.slice(flag.length);
|
||||
}
|
||||
|
||||
while (system_string_endswith(str, flag)) {
|
||||
str = str.slice(0, str.length - flag.length);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* array sort asc, for example:
|
||||
* [a, b] in [10, 11, 9]
|
||||
* then sort to: [9, 10, 11]
|
||||
* Usage, for example:
|
||||
obj.data.data.sort(function(a, b){
|
||||
return array_sort_asc(a.metadata.meta_id, b.metadata.meta_id);
|
||||
});
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* @remark, if need desc, use -1*array_sort_asc(a,b)
|
||||
*/
|
||||
function array_sort_asc(elem_a, elem_b) {
|
||||
if (elem_a > elem_b) {
|
||||
return 1;
|
||||
}
|
||||
return (elem_a < elem_b)? -1 : 0;
|
||||
}
|
||||
function array_sort_desc(elem_a, elem_b) {
|
||||
return -1 * array_sort_asc(elem_a, elem_b);
|
||||
}
|
||||
function system_array_sort_asc(elem_a, elem_b) {
|
||||
return array_sort_asc(elem_a, elem_b);
|
||||
}
|
||||
function system_array_sort_desc(elem_a, elem_b) {
|
||||
return -1 * array_sort_asc(elem_a, elem_b);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the query string to object.
|
||||
* parse the url location object as: host(hostname:http_port), pathname(dir/filename)
|
||||
* for example, url http://192.168.1.168:1980/ui/players.html?vhost=player.vhost.com&app=test&stream=livestream
|
||||
* parsed to object:
|
||||
{
|
||||
host : "192.168.1.168:1980",
|
||||
hostname : "192.168.1.168",
|
||||
http_port : 1980,
|
||||
pathname : "/ui/players.html",
|
||||
dir : "/ui",
|
||||
filename : "/players.html",
|
||||
|
||||
vhost : "player.vhost.com",
|
||||
app : "test",
|
||||
stream : "livestream"
|
||||
}
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
*/
|
||||
function parse_query_string(){
|
||||
var obj = {};
|
||||
|
||||
// add the uri object.
|
||||
// parse the host(hostname:http_port), pathname(dir/filename)
|
||||
obj.host = window.location.host;
|
||||
obj.hostname = window.location.hostname;
|
||||
obj.http_port = (window.location.port == "")? 80:window.location.port;
|
||||
obj.pathname = window.location.pathname;
|
||||
if (obj.pathname.lastIndexOf("/") <= 0) {
|
||||
obj.dir = "/";
|
||||
obj.filename = "";
|
||||
} else {
|
||||
obj.dir = obj.pathname.slice(0, obj.pathname.lastIndexOf("/"));
|
||||
obj.filename = obj.pathname.slice(obj.pathname.lastIndexOf("/"));
|
||||
}
|
||||
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
// parse the query string.
|
||||
var query_string = String(window.location.search).replace(" ", "").split("?")[1];
|
||||
if(query_string === undefined){
|
||||
query_string = String(window.location.hash).replace(" ", "").split("#")[1];
|
||||
if(query_string === undefined){
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
__fill_query(query_string, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function __fill_query(query_string, obj) {
|
||||
// pure user query object.
|
||||
obj.user_query = {};
|
||||
|
||||
if (query_string.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// split again for angularjs.
|
||||
if (query_string.indexOf("?") >= 0) {
|
||||
query_string = query_string.split("?")[1];
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var elem = queries[i];
|
||||
|
||||
var query = elem.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
obj.user_query[query[0]] = query[1];
|
||||
}
|
||||
|
||||
// alias domain for vhost.
|
||||
if (obj.domain) {
|
||||
obj.vhost = obj.domain;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the rtmp url,
|
||||
* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
|
||||
* @return object {server, port, vhost, app, stream}
|
||||
* for exmaple, rtmp_url is rtmp://demo.srs.com:1935/live...vhost...players/livestream
|
||||
* parsed to object:
|
||||
{
|
||||
server: "demo.srs.com",
|
||||
port: 1935,
|
||||
vhost: "players",
|
||||
app: "live",
|
||||
stream: "livestream"
|
||||
}
|
||||
*/
|
||||
function parse_rtmp_url(rtmp_url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = rtmp_url.replace("rtmp://", "http://")
|
||||
.replace("webrtc://", "http://")
|
||||
.replace("rtc://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var app = a.pathname.substring(1, a.pathname.lastIndexOf("/"));
|
||||
var stream = a.pathname.slice(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.slice(app.indexOf("?"));
|
||||
app = app.slice(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.slice(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when vhost equals to server, and server is ip,
|
||||
// the vhost is __defaultVhost__
|
||||
if (a.hostname === vhost) {
|
||||
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
|
||||
if (re.test(a.hostname)) {
|
||||
vhost = "__defaultVhost__";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the schema
|
||||
var schema = "rtmp";
|
||||
if (rtmp_url.indexOf("://") > 0) {
|
||||
schema = rtmp_url.slice(0, rtmp_url.indexOf("://"));
|
||||
}
|
||||
|
||||
var port = a.port;
|
||||
if (!port) {
|
||||
if (schema === 'http') {
|
||||
port = 80;
|
||||
} else if (schema === 'https') {
|
||||
port = 443;
|
||||
} else if (schema === 'rtmp') {
|
||||
port = 1935;
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
url: rtmp_url,
|
||||
schema: schema,
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
__fill_query(a.search, ret);
|
||||
|
||||
// For webrtc API, we use 443 if page is https, or schema specified it.
|
||||
if (!ret.port) {
|
||||
if (schema === 'webrtc' || schema === 'rtc') {
|
||||
if (ret.user_query.schema === 'https') {
|
||||
ret.port = 443;
|
||||
} else if (window.location.href.indexOf('https://') === 0) {
|
||||
ret.port = 443;
|
||||
} else {
|
||||
// For WebRTC, SRS use 1985 as default API port.
|
||||
ret.port = 1985;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the agent.
|
||||
* @return an object specifies some browser.
|
||||
* for example, get_browser_agents().MSIE
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
*/
|
||||
function get_browser_agents() {
|
||||
var agent = navigator.userAgent;
|
||||
|
||||
/**
|
||||
WindowsPC platform, Win7:
|
||||
chrome 31.0.1650.63:
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
|
||||
(KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
|
||||
firefox 23.0.1:
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101
|
||||
Firefox/23.0
|
||||
safari 5.1.7(7534.57.2):
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2
|
||||
(KHTML, like Gecko) Version/5.1.7 Safari/534.57.2
|
||||
opera 15.0.1147.153:
|
||||
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
|
||||
(KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
|
||||
OPR/15.0.1147.153
|
||||
360 6.2.1.272:
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
|
||||
Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
|
||||
.NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
|
||||
.NET4.0E)
|
||||
IE 10.0.9200.16750(update: 10.0.12):
|
||||
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
|
||||
Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
|
||||
.NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
|
||||
.NET4.0E)
|
||||
*/
|
||||
|
||||
return {
|
||||
// platform
|
||||
Android: agent.indexOf("Android") != -1,
|
||||
Windows: agent.indexOf("Windows") != -1,
|
||||
iPhone: agent.indexOf("iPhone") != -1,
|
||||
// Windows Browsers
|
||||
Chrome: agent.indexOf("Chrome") != -1,
|
||||
Firefox: agent.indexOf("Firefox") != -1,
|
||||
QQBrowser: agent.indexOf("QQBrowser") != -1,
|
||||
MSIE: agent.indexOf("MSIE") != -1,
|
||||
// Android Browsers
|
||||
Opera: agent.indexOf("Presto") != -1,
|
||||
MQQBrowser: agent.indexOf("MQQBrowser") != -1
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* format relative seconds to HH:MM:SS,
|
||||
* for example, 210s formated to 00:03:30
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* @usage relative_seconds_to_HHMMSS(210)
|
||||
*/
|
||||
function relative_seconds_to_HHMMSS(seconds){
|
||||
var date = new Date();
|
||||
date.setTime(Number(seconds) * 1000);
|
||||
|
||||
var ret = padding(date.getUTCHours(), 2, '0')
|
||||
+ ":" + padding(date.getUTCMinutes(), 2, '0')
|
||||
+ ":" + padding(date.getUTCSeconds(), 2, '0');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* format absolute seconds to HH:MM:SS,
|
||||
* for example, 1389146480s (2014-01-08 10:01:20 GMT+0800) formated to 10:01:20
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* @usage absolute_seconds_to_HHMMSS(new Date().getTime() / 1000)
|
||||
*/
|
||||
function absolute_seconds_to_HHMMSS(seconds){
|
||||
var date = new Date();
|
||||
date.setTime(Number(seconds) * 1000);
|
||||
|
||||
var ret = padding(date.getHours(), 2, '0')
|
||||
+ ":" + padding(date.getMinutes(), 2, '0')
|
||||
+ ":" + padding(date.getSeconds(), 2, '0');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* format absolute seconds to YYYY-mm-dd,
|
||||
* for example, 1389146480s (2014-01-08 10:01:20 GMT+0800) formated to 2014-01-08
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* @usage absolute_seconds_to_YYYYmmdd(new Date().getTime() / 1000)
|
||||
*/
|
||||
function absolute_seconds_to_YYYYmmdd(seconds) {
|
||||
var date = new Date();
|
||||
date.setTime(Number(seconds) * 1000);
|
||||
|
||||
var ret = date.getFullYear()
|
||||
+ "-" + padding(date.getMonth() + 1, 2, '0')
|
||||
+ "-" + padding(date.getDate(), 2, '0');
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the date in str to Date object.
|
||||
* @param str the date in str, format as "YYYY-mm-dd", for example, 2014-12-11
|
||||
* @returns a date object.
|
||||
* @usage YYYYmmdd_parse("2014-12-11")
|
||||
*/
|
||||
function YYYYmmdd_parse(str) {
|
||||
var date = new Date();
|
||||
date.setTime(Date.parse(str));
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* async refresh function call. to avoid multiple call.
|
||||
* @remark AsyncRefresh is for jquery to refresh the speicified pfn in a page;
|
||||
* if angularjs, use AsyncRefresh2 to change pfn, cancel previous request for angularjs use singleton object.
|
||||
* @param refresh_interval the default refresh interval ms.
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* the pfn can be implements as following:
|
||||
var async_refresh = new AsyncRefresh(pfn, 3000);
|
||||
function pfn() {
|
||||
if (!async_refresh.refresh_is_enabled()) {
|
||||
async_refresh.request(100);
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: 'GET', async: true, url: 'xxxxx',
|
||||
complete: function(){
|
||||
if (!async_refresh.refresh_is_enabled()) {
|
||||
async_refresh.request(0);
|
||||
} else {
|
||||
async_refresh.request(async_refresh.refresh_interval);
|
||||
}
|
||||
},
|
||||
success: function(res){
|
||||
// if donot allow refresh, directly return.
|
||||
if (!async_refresh.refresh_is_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// render the res.
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
function AsyncRefresh(pfn, refresh_interval) {
|
||||
this.refresh_interval = refresh_interval;
|
||||
|
||||
this.__handler = null;
|
||||
this.__pfn = pfn;
|
||||
|
||||
this.__enabled = true;
|
||||
}
|
||||
/**
|
||||
* disable the refresher, the pfn must check the refresh state.
|
||||
*/
|
||||
AsyncRefresh.prototype.refresh_disable = function() {
|
||||
this.__enabled = false;
|
||||
}
|
||||
AsyncRefresh.prototype.refresh_enable = function() {
|
||||
this.__enabled = true;
|
||||
}
|
||||
AsyncRefresh.prototype.refresh_is_enabled = function() {
|
||||
return this.__enabled;
|
||||
}
|
||||
/**
|
||||
* start new async request
|
||||
* @param timeout the timeout in ms.
|
||||
* user can use the refresh_interval of the AsyncRefresh object,
|
||||
* which initialized in constructor.
|
||||
*/
|
||||
AsyncRefresh.prototype.request = function(timeout) {
|
||||
if (this.__handler) {
|
||||
clearTimeout(this.__handler);
|
||||
}
|
||||
|
||||
this.__handler = setTimeout(this.__pfn, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* async refresh v2, support cancellable refresh, and change the refresh pfn.
|
||||
* @remakr for angularjs. if user only need jquery, maybe AsyncRefresh is better.
|
||||
* @see: http://blog.csdn.net/win_lin/article/details/17994347
|
||||
* Usage:
|
||||
bsmControllers.controller('CServers', ['$scope', 'MServer', function($scope, MServer){
|
||||
async_refresh2.refresh_change(function(){
|
||||
// 获取服务器列表
|
||||
MServer.servers_load({}, function(data){
|
||||
$scope.servers = data.data.servers;
|
||||
async_refresh2.request();
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
async_refresh2.request(0);
|
||||
}]);
|
||||
bsmControllers.controller('CStreams', ['$scope', 'MStream', function($scope, MStream){
|
||||
async_refresh2.refresh_change(function(){
|
||||
// 获取流列表
|
||||
MStream.streams_load({}, function(data){
|
||||
$scope.streams = data.data.streams;
|
||||
async_refresh2.request();
|
||||
});
|
||||
}, 3000);
|
||||
|
||||
async_refresh2.request(0);
|
||||
}]);
|
||||
*/
|
||||
function AsyncRefresh2() {
|
||||
/**
|
||||
* the function callback before call the pfn.
|
||||
* the protype is function():bool, which return true to invoke, false to abort the call.
|
||||
* null to ignore this callback.
|
||||
*
|
||||
* for example, user can abort the refresh by find the class popover:
|
||||
* async_refresh2.on_before_call_pfn = function() {
|
||||
* if ($(".popover").length > 0) {
|
||||
* async_refresh2.request();
|
||||
* return false;
|
||||
* }
|
||||
* return true;
|
||||
* };
|
||||
*/
|
||||
this.on_before_call_pfn = null;
|
||||
|
||||
// use a anonymous function to call, and check the enabled when actually invoke.
|
||||
this.__call = {
|
||||
pfn: null,
|
||||
timeout: 0,
|
||||
__enabled: false,
|
||||
__handler: null
|
||||
};
|
||||
}
|
||||
// singleton
|
||||
var async_refresh2 = new AsyncRefresh2();
|
||||
/**
|
||||
* initialize or refresh change. cancel previous request, setup new request.
|
||||
* @param pfn a function():void to request after timeout. null to disable refresher.
|
||||
* @param timeout the timeout in ms, to call pfn. null to disable refresher.
|
||||
*/
|
||||
AsyncRefresh2.prototype.initialize = function(pfn, timeout) {
|
||||
this.refresh_change(pfn, timeout);
|
||||
}
|
||||
/**
|
||||
* stop refresh, the refresh pfn is set to null.
|
||||
*/
|
||||
AsyncRefresh2.prototype.stop = function() {
|
||||
this.__call.__enabled = false;
|
||||
}
|
||||
/**
|
||||
* restart refresh, use previous config.
|
||||
*/
|
||||
AsyncRefresh2.prototype.restart = function() {
|
||||
this.__call.__enabled = true;
|
||||
this.request(0);
|
||||
}
|
||||
/**
|
||||
* change refresh pfn, the old pfn will set to disabled.
|
||||
*/
|
||||
AsyncRefresh2.prototype.refresh_change = function(pfn, timeout) {
|
||||
// cancel the previous call.
|
||||
if (this.__call.__handler) {
|
||||
clearTimeout(this.__handler);
|
||||
}
|
||||
this.__call.__enabled = false;
|
||||
|
||||
// setup new call.
|
||||
this.__call = {
|
||||
pfn: pfn,
|
||||
timeout: timeout,
|
||||
__enabled: true,
|
||||
__handler: null
|
||||
};
|
||||
}
|
||||
/**
|
||||
* start new request, we never auto start the request,
|
||||
* user must start new request when previous completed.
|
||||
* @param timeout [optional] if not specified, use the timeout in initialize or refresh_change.
|
||||
*/
|
||||
AsyncRefresh2.prototype.request = function(timeout) {
|
||||
var self = this;
|
||||
var this_call = this.__call;
|
||||
|
||||
// clear previous timeout.
|
||||
if (this_call.__handler) {
|
||||
clearTimeout(this_call.__handler);
|
||||
}
|
||||
|
||||
// override the timeout
|
||||
if (timeout == undefined) {
|
||||
timeout = this_call.timeout;
|
||||
}
|
||||
|
||||
// if user disabled refresher.
|
||||
if (this_call.pfn == null || timeout == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this_call.__handler = setTimeout(function(){
|
||||
// cancelled by refresh_change, ignore.
|
||||
if (!this_call.__enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// callback if the handler installled.
|
||||
if (self.on_before_call_pfn) {
|
||||
if (!self.on_before_call_pfn()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// do the actual call.
|
||||
this_call.pfn();
|
||||
}, timeout);
|
||||
}
|
||||
|
63
src/api/drone/droneConfig/index.ts
Normal file
63
src/api/drone/droneConfig/index.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DroneConfigVO, DroneConfigForm, DroneConfigQuery } from '@/api/drone/droneConfig/types';
|
||||
|
||||
/**
|
||||
* 查询无人机配置列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listDroneConfig = (query?: DroneConfigQuery): AxiosPromise<DroneConfigVO[]> => {
|
||||
return request({
|
||||
url: '/drone/droneConfig/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询无人机配置详细
|
||||
* @param id
|
||||
*/
|
||||
export const getDroneConfig = (id: string | number): AxiosPromise<DroneConfigVO> => {
|
||||
return request({
|
||||
url: '/drone/droneConfig/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增无人机配置
|
||||
* @param data
|
||||
*/
|
||||
export const addDroneConfig = (data: DroneConfigForm) => {
|
||||
return request({
|
||||
url: '/drone/droneConfig',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改无人机配置
|
||||
* @param data
|
||||
*/
|
||||
export const updateDroneConfig = (data: DroneConfigForm) => {
|
||||
return request({
|
||||
url: '/drone/droneConfig',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除无人机配置
|
||||
* @param id
|
||||
*/
|
||||
export const delDroneConfig = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/drone/droneConfig/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
80
src/api/drone/droneConfig/types.ts
Normal file
80
src/api/drone/droneConfig/types.ts
Normal file
@ -0,0 +1,80 @@
|
||||
export interface DroneConfigVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
projectId: string | number;
|
||||
|
||||
/**
|
||||
* 配置名称
|
||||
*/
|
||||
configName: string;
|
||||
|
||||
/**
|
||||
* 配置地址
|
||||
*/
|
||||
configUrl: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface DroneConfigForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
projectId?: string | number;
|
||||
|
||||
/**
|
||||
* 配置名称
|
||||
*/
|
||||
configName?: string;
|
||||
|
||||
/**
|
||||
* 配置地址
|
||||
*/
|
||||
configUrl?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
dockSocketUrl?: string;
|
||||
aiUrl?: string;
|
||||
srsUrl?: string;
|
||||
rtmpPort?: string;
|
||||
rtcPort?: string;
|
||||
}
|
||||
|
||||
export interface DroneConfigQuery extends PageQuery {
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
projectId?: string | number;
|
||||
|
||||
/**
|
||||
* 配置名称
|
||||
*/
|
||||
configName?: string;
|
||||
|
||||
/**
|
||||
* 配置地址
|
||||
*/
|
||||
configUrl?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
@ -23,7 +23,7 @@ import { AttendanceMonthVO } from '../attendance/types';
|
||||
|
||||
export const listConstructionMonth = (query?: ConstructionMonthQuery): AxiosPromise<AttendanceMonthVO[]> => {
|
||||
return request({
|
||||
url: '/project/constructionUser/list/attendance/month',
|
||||
url: '/contractor/constructionUser/list/attendance/month',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@ -36,7 +36,7 @@ export const listConstructionMonth = (query?: ConstructionMonthQuery): AxiosProm
|
||||
|
||||
export const listConstructionUser = (query?: ConstructionUserQuery): AxiosPromise<ConstructionUserVO[]> => {
|
||||
return request({
|
||||
url: '/project/constructionUser/list',
|
||||
url: '/contractor/constructionUser/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@ -48,7 +48,7 @@ export const listConstructionUser = (query?: ConstructionUserQuery): AxiosPromis
|
||||
*/
|
||||
export const getConstructionUser = (id: string | number): AxiosPromise<ConstructionUserVO> => {
|
||||
return request({
|
||||
url: '/project/constructionUser/' + id,
|
||||
url: '/contractor/constructionUser/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@ -59,7 +59,7 @@ export const getConstructionUser = (id: string | number): AxiosPromise<Construct
|
||||
*/
|
||||
export const transferConstructionUser = (data: skipType) => {
|
||||
return request({
|
||||
url: '/project/constructionUser/change/project',
|
||||
url: '/contractor/constructionUser/change/project',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -81,7 +81,7 @@ export const getProjectContractorList = () => {
|
||||
*/
|
||||
export const addConstructionUser = (data: ConstructionUserForm): AxiosPromise<string | number> => {
|
||||
return request({
|
||||
url: '/project/constructionUser',
|
||||
url: '/contractor/constructionUser',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
@ -93,7 +93,7 @@ export const addConstructionUser = (data: ConstructionUserForm): AxiosPromise<st
|
||||
*/
|
||||
export const updateConstructionUser = (data: ConstructionUserForm) => {
|
||||
return request({
|
||||
url: '/project/constructionUser',
|
||||
url: '/contractor/constructionUser',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -105,7 +105,7 @@ export const updateConstructionUser = (data: ConstructionUserForm) => {
|
||||
*/
|
||||
export const delConstructionUser = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/project/constructionUser/' + id,
|
||||
url: '/contractor/constructionUser/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
@ -116,7 +116,7 @@ export const delConstructionUser = (id: string | number | Array<string | number>
|
||||
*/
|
||||
export const updateConstructionUserStatus = (data: ConstructionUserStatusForm) => {
|
||||
return request({
|
||||
url: '/project/constructionUser/batch/status',
|
||||
url: '/contractor/constructionUser/batch/status',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -128,7 +128,7 @@ export const updateConstructionUserStatus = (data: ConstructionUserStatusForm) =
|
||||
*/
|
||||
export const updateConstructionUserPlayCardStatus = (data: ConstructionUserPlayCardForm) => {
|
||||
return request({
|
||||
url: '/project/constructionUser/batch/clock',
|
||||
url: '/contractor/constructionUser/batch/clock',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -140,7 +140,7 @@ export const updateConstructionUserPlayCardStatus = (data: ConstructionUserPlayC
|
||||
*/
|
||||
export const updateConstructionUserPlayCardOneStatus = (data: ConstructionUserPlayCardForm) => {
|
||||
return request({
|
||||
url: '/project/constructionUser/clock',
|
||||
url: '/contractor/constructionUser/clock',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -152,7 +152,7 @@ export const updateConstructionUserPlayCardOneStatus = (data: ConstructionUserPl
|
||||
*/
|
||||
export const updateConstructionUserSalary = (data: ConstructionUserSalaryForm) => {
|
||||
return request({
|
||||
url: '/project/constructionUser/salary',
|
||||
url: '/contractor/constructionUser/salary',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -164,7 +164,7 @@ export const updateConstructionUserSalary = (data: ConstructionUserSalaryForm) =
|
||||
*/
|
||||
export const getConstructionUserExit = (query: ConstructionUserExitForm) => {
|
||||
return request({
|
||||
url: '/project/constructionUserExit/list',
|
||||
url: '/contractor/constructionUserExit/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@ -177,7 +177,7 @@ export const getConstructionUserExit = (query: ConstructionUserExitForm) => {
|
||||
export const dowloadConstructionUserTemplate = (query: ConstructionUserTemplateForm) => {
|
||||
let { projectId } = query;
|
||||
const fileName = projectId + '_project.zip';
|
||||
return download('/project/constructionUserFile/exportFileTemplate', query, fileName);
|
||||
return download('/contractor/constructionUserFile/exportFileTemplate', query, fileName);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -186,7 +186,7 @@ export const dowloadConstructionUserTemplate = (query: ConstructionUserTemplateF
|
||||
*/
|
||||
export const delConstructionUserMember = (data: ConstructionUserMembeForm) => {
|
||||
return request({
|
||||
url: '/project/projectTeamMember/',
|
||||
url: '/contractor/projectTeamMember/',
|
||||
method: 'delete',
|
||||
data
|
||||
});
|
||||
@ -198,7 +198,7 @@ export const delConstructionUserMember = (data: ConstructionUserMembeForm) => {
|
||||
*/
|
||||
export const importConstructionUserInfo = (file: string) => {
|
||||
return request({
|
||||
url: '/project/constructionUserFile/upload/zip',
|
||||
url: '/contractor/constructionUserFile/upload/zip',
|
||||
method: 'post',
|
||||
data: { file }
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ import { ContractorForm, ContractorQuery, ContractorVO } from '@/api/project/con
|
||||
|
||||
export const listContractor = (query?: ContractorQuery): AxiosPromise<ContractorVO[]> => {
|
||||
return request({
|
||||
url: '/project/contractor/list',
|
||||
url: '/contractor/contractor/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@ -22,7 +22,7 @@ export const listContractor = (query?: ContractorQuery): AxiosPromise<Contractor
|
||||
*/
|
||||
export const getContractor = (id: string | number): AxiosPromise<ContractorVO> => {
|
||||
return request({
|
||||
url: '/project/contractor/' + id,
|
||||
url: '/contractor/contractor/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@ -33,7 +33,7 @@ export const getContractor = (id: string | number): AxiosPromise<ContractorVO> =
|
||||
*/
|
||||
export const addContractor = (data: ContractorForm): AxiosPromise<string | number> => {
|
||||
return request({
|
||||
url: '/project/contractor',
|
||||
url: '/contractor/contractor',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
@ -45,7 +45,7 @@ export const addContractor = (data: ContractorForm): AxiosPromise<string | numbe
|
||||
*/
|
||||
export const updateContractor = (data: ContractorForm) => {
|
||||
return request({
|
||||
url: '/project/contractor',
|
||||
url: '/contractor/contractor',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -57,7 +57,7 @@ export const updateContractor = (data: ContractorForm) => {
|
||||
*/
|
||||
export const delContractor = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/project/contractor/' + id,
|
||||
url: '/contractor/contractor/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { ContractorMaterialVO, ContractorMaterialForm, ContractorMaterialQuery } from '@/api/contractor/contractorMaterial/types';
|
||||
import { ContractorMaterialVO, ContractorMaterialForm, ContractorMaterialQuery } from '@/api/project/contractorMaterial/types';
|
||||
|
||||
/**
|
||||
* 查询分包方物料列表
|
||||
|
@ -10,7 +10,7 @@ import { ContractorToolVO, ContractorToolForm, ContractorToolQuery } from '@/api
|
||||
|
||||
export const listContractorTool = (query?: ContractorToolQuery): AxiosPromise<ContractorToolVO[]> => {
|
||||
return request({
|
||||
url: '/project/contractorTool/list',
|
||||
url: '/contractor/contractorTool/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@ -22,7 +22,7 @@ export const listContractorTool = (query?: ContractorToolQuery): AxiosPromise<Co
|
||||
*/
|
||||
export const getContractorTool = (id: string | number): AxiosPromise<ContractorToolVO> => {
|
||||
return request({
|
||||
url: '/project/contractorTool/' + id,
|
||||
url: '/contractor/contractorTool/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@ -33,7 +33,7 @@ export const getContractorTool = (id: string | number): AxiosPromise<ContractorT
|
||||
*/
|
||||
export const addContractorTool = (data: ContractorToolForm) => {
|
||||
return request({
|
||||
url: '/project/contractorTool',
|
||||
url: '/contractor/contractorTool',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
@ -45,7 +45,7 @@ export const addContractorTool = (data: ContractorToolForm) => {
|
||||
*/
|
||||
export const updateContractorTool = (data: ContractorToolForm) => {
|
||||
return request({
|
||||
url: '/project/contractorTool',
|
||||
url: '/contractor/contractorTool',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -57,7 +57,7 @@ export const updateContractorTool = (data: ContractorToolForm) => {
|
||||
*/
|
||||
export const delContractorTool = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/project/contractorTool/' + id,
|
||||
url: '/contractor/contractorTool/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ import { SubcontractVO, SubcontractForm, SubcontractQuery } from '@/api/project/
|
||||
|
||||
export const listSubcontract = (query?: SubcontractQuery): AxiosPromise<SubcontractVO[]> => {
|
||||
return request({
|
||||
url: '/project/subcontract/list',
|
||||
url: '/contractor/subcontract/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
@ -22,7 +22,7 @@ export const listSubcontract = (query?: SubcontractQuery): AxiosPromise<Subcontr
|
||||
*/
|
||||
export const getSubcontract = (id: string | number): AxiosPromise<SubcontractVO> => {
|
||||
return request({
|
||||
url: '/project/subcontract/' + id,
|
||||
url: '/contractor/subcontract/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@ -33,7 +33,7 @@ export const getSubcontract = (id: string | number): AxiosPromise<SubcontractVO>
|
||||
*/
|
||||
export const addSubcontract = (data: SubcontractForm) => {
|
||||
return request({
|
||||
url: '/project/subcontract',
|
||||
url: '/contractor/subcontract',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
@ -45,7 +45,7 @@ export const addSubcontract = (data: SubcontractForm) => {
|
||||
*/
|
||||
export const updateSubcontract = (data: SubcontractForm) => {
|
||||
return request({
|
||||
url: '/project/subcontract',
|
||||
url: '/contractor/subcontract',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
@ -57,7 +57,7 @@ export const updateSubcontract = (data: SubcontractForm) => {
|
||||
*/
|
||||
export const delSubcontract = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/project/subcontract/' + id,
|
||||
url: '/contractor/subcontract/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
63
src/api/safety/violationRecord/index.ts
Normal file
63
src/api/safety/violationRecord/index.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { ViolationRecordVO, ViolationRecordForm, ViolationRecordQuery } from '@/api/safety/violationRecord/types';
|
||||
|
||||
/**
|
||||
* 查询违规记录列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listViolationRecord = (query?: ViolationRecordQuery): AxiosPromise<ViolationRecordVO[]> => {
|
||||
return request({
|
||||
url: '/safety/violationRecord/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询违规记录详细
|
||||
* @param id
|
||||
*/
|
||||
export const getViolationRecord = (id: string | number): AxiosPromise<ViolationRecordVO> => {
|
||||
return request({
|
||||
url: '/safety/violationRecord/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增违规处理人
|
||||
* @param data
|
||||
*/
|
||||
export const addViolationRecord = (data: any) => {
|
||||
return request({
|
||||
url: '/safety/violationRecord/handler',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改违规记录
|
||||
* @param data
|
||||
*/
|
||||
export const updateViolationRecord = (data: ViolationRecordForm) => {
|
||||
return request({
|
||||
url: '/safety/violationRecord',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除违规记录
|
||||
* @param id
|
||||
*/
|
||||
export const delViolationRecord = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/safety/violationRecord/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
241
src/api/safety/violationRecord/types.ts
Normal file
241
src/api/safety/violationRecord/types.ts
Normal file
@ -0,0 +1,241 @@
|
||||
export interface ViolationRecordVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
projectId: string | number;
|
||||
|
||||
/**
|
||||
* 违章等级id
|
||||
*/
|
||||
levelId: string | number;
|
||||
|
||||
/**
|
||||
* 违章类型
|
||||
*/
|
||||
violationType: string;
|
||||
|
||||
/**
|
||||
* 违章时间
|
||||
*/
|
||||
violationTime: string;
|
||||
|
||||
/**
|
||||
* 违章处理人id
|
||||
*/
|
||||
handlerId: string | number;
|
||||
|
||||
/**
|
||||
* 整改措施
|
||||
*/
|
||||
measure: string;
|
||||
|
||||
/**
|
||||
* 整改时间
|
||||
*/
|
||||
rectificationTime: string;
|
||||
|
||||
/**
|
||||
* 复查情况
|
||||
*/
|
||||
review: string;
|
||||
|
||||
/**
|
||||
* 复查状态(1通过 2未通过)
|
||||
*/
|
||||
reviewType: string;
|
||||
|
||||
/**
|
||||
* 复查时间
|
||||
*/
|
||||
reviewTime: string;
|
||||
|
||||
/**
|
||||
* 处理流程类型(0仅通知 1通知整改复查)
|
||||
*/
|
||||
processType: string;
|
||||
|
||||
/**
|
||||
* 工单状态(1通知 2整改 3复查)
|
||||
*/
|
||||
status: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
}
|
||||
|
||||
export interface ViolationRecordForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
projectId?: string | number;
|
||||
|
||||
/**
|
||||
* 违章等级id
|
||||
*/
|
||||
levelId?: string | number;
|
||||
|
||||
/**
|
||||
* 识别记录id
|
||||
*/
|
||||
recognizeId?: string | number;
|
||||
|
||||
/**
|
||||
* 违章类型
|
||||
*/
|
||||
violationType?: string;
|
||||
|
||||
/**
|
||||
* 违章时间
|
||||
*/
|
||||
violationTime?: string;
|
||||
|
||||
/**
|
||||
* 违章处理人id
|
||||
*/
|
||||
handlerId?: string | number;
|
||||
|
||||
/**
|
||||
* 处理期限
|
||||
*/
|
||||
disposeDeadline?: string;
|
||||
|
||||
/**
|
||||
* 处理时间
|
||||
*/
|
||||
disposeTime?: string;
|
||||
|
||||
/**
|
||||
* 整改措施
|
||||
*/
|
||||
measure?: string;
|
||||
|
||||
/**
|
||||
* 整改时间
|
||||
*/
|
||||
rectificationTime?: string;
|
||||
|
||||
/**
|
||||
* 复查情况
|
||||
*/
|
||||
review?: string;
|
||||
|
||||
/**
|
||||
* 复查状态(1通过 2未通过)
|
||||
*/
|
||||
reviewType?: string;
|
||||
|
||||
/**
|
||||
* 复查时间
|
||||
*/
|
||||
reviewTime?: string;
|
||||
|
||||
/**
|
||||
* 处理流程类型(0仅通知 1通知整改复查)
|
||||
*/
|
||||
processType?: string;
|
||||
|
||||
/**
|
||||
* 工单状态(1通知 2整改 3复查)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface ViolationRecordQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
projectId?: string | number;
|
||||
|
||||
/**
|
||||
* 违章类型
|
||||
*/
|
||||
violationType?: string;
|
||||
|
||||
/**
|
||||
* 违章时间
|
||||
*/
|
||||
violationTime?: string;
|
||||
|
||||
/**
|
||||
* 违章处理人id
|
||||
*/
|
||||
handlerId?: string | number;
|
||||
|
||||
/**
|
||||
* 处理期限
|
||||
*/
|
||||
disposeDeadline?: string;
|
||||
|
||||
/**
|
||||
* 处理时间
|
||||
*/
|
||||
disposeTime?: string;
|
||||
|
||||
/**
|
||||
* 整改措施
|
||||
*/
|
||||
measure?: string;
|
||||
|
||||
/**
|
||||
* 整改时间
|
||||
*/
|
||||
rectificationTime?: string;
|
||||
|
||||
/**
|
||||
* 复查情况
|
||||
*/
|
||||
review?: string;
|
||||
|
||||
/**
|
||||
* 复查状态(1通过 2未通过)
|
||||
*/
|
||||
reviewType?: string;
|
||||
|
||||
/**
|
||||
* 复查时间
|
||||
*/
|
||||
reviewTime?: string;
|
||||
|
||||
/**
|
||||
* 处理流程类型(0仅通知 1通知整改复查)
|
||||
*/
|
||||
processType?: string;
|
||||
|
||||
/**
|
||||
* 工单状态(1通知 2整改 3复查)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import {DeptForm, DeptQuery, DeptTreeVO, DeptVO} from './types';
|
||||
import { DeptForm, DeptQuery, DeptTreeVO, DeptVO } from './types';
|
||||
|
||||
// 查询部门列表
|
||||
export const listDept = (query?: DeptQuery) => {
|
||||
@ -11,6 +11,24 @@ export const listDept = (query?: DeptQuery) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeptList = () => {
|
||||
return request({
|
||||
url: '/project/project/listNoDept',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询分包单位
|
||||
* @param projectId
|
||||
*/
|
||||
export const optionProjectSelect = (projectId: number | string): any => {
|
||||
return request({
|
||||
url: '/contractor/contractor/listNoDept/' + projectId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过deptIds查询部门
|
||||
* @param deptIds
|
||||
|
@ -5,6 +5,8 @@ export interface DeptQuery extends PageQuery {
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
status?: number;
|
||||
deptType?: string;
|
||||
isShow?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -16,6 +18,9 @@ export interface DeptVO extends BaseEntity {
|
||||
parentId: number | string;
|
||||
children: DeptVO[];
|
||||
deptId: number | string;
|
||||
contractorList: any[];
|
||||
projectId: number | string;
|
||||
projectList: any[];
|
||||
deptName: string;
|
||||
deptCategory: string;
|
||||
orderNum: number;
|
||||
@ -48,10 +53,15 @@ export interface DeptForm {
|
||||
parentId?: number | string;
|
||||
children?: DeptForm[];
|
||||
deptId?: number | string;
|
||||
projectId?: number | string;
|
||||
contractorId?: number | string;
|
||||
rowProjectId?: number | string;
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
orderNum?: number;
|
||||
leader?: string;
|
||||
deptType?: string;
|
||||
isShow?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
status?: string;
|
||||
|
@ -11,6 +11,14 @@ export function listPost(query: { pageNum: number; pageSize: number }): AxiosPro
|
||||
});
|
||||
}
|
||||
|
||||
// 查询岗位列表
|
||||
export function listTreeByProject(projectId: string): AxiosPromise<PostVO[]> {
|
||||
return request({
|
||||
url: '/system/dept/list/treeByProjectId/' + projectId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询岗位详细
|
||||
export function getPost(postId: string | number): AxiosPromise<PostVO> {
|
||||
return request({
|
||||
@ -56,3 +64,14 @@ export function delPost(postId: string | number | (string | number)[]) {
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取岗位选择框列表
|
||||
export function getRoleList(deptId?: number | string): AxiosPromise<any[]> {
|
||||
return request({
|
||||
url: '/system/role/listNoPage',
|
||||
method: 'get',
|
||||
params: {
|
||||
deptId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ export interface DeptTreeOption {
|
||||
label: string;
|
||||
parentId: string;
|
||||
weight: number;
|
||||
children?: DeptTreeOption[];
|
||||
children: DeptTreeOption[];
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export interface RoleDeptTree {
|
||||
@ -34,6 +35,7 @@ export interface RoleVO extends BaseEntity {
|
||||
export interface RoleQuery extends PageQuery {
|
||||
roleName: string;
|
||||
roleKey: string;
|
||||
deptId: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@ -46,7 +48,9 @@ export interface RoleForm {
|
||||
deptCheckStrictly: boolean;
|
||||
remark: string;
|
||||
dataScope?: string;
|
||||
isSpecial: string;
|
||||
roleId: string | undefined;
|
||||
menuIds: Array<string | number>;
|
||||
deptIds: Array<string | number>;
|
||||
deptId?: string;
|
||||
}
|
||||
|
@ -202,10 +202,11 @@ export const listUserByDeptId = (deptId: string | number): AxiosPromise<UserVO[]
|
||||
/**
|
||||
* 查询部门下拉树结构
|
||||
*/
|
||||
export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
|
||||
export const deptTreeSelect = (data?: { isShow: string }): AxiosPromise<DeptTreeVO[]> => {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: data
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ export interface FlowDefinitionQuery extends PageQuery {
|
||||
flowName?: string;
|
||||
category: string | number;
|
||||
isPublish?: number;
|
||||
projectId: string | number;
|
||||
}
|
||||
|
||||
export interface FlowDefinitionVo {
|
||||
@ -23,6 +24,7 @@ export interface FlowDefinitionForm {
|
||||
flowCode: string;
|
||||
category: string;
|
||||
formPath: string;
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
export interface definitionXmlVO {
|
||||
|
@ -2,9 +2,11 @@ import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
|
||||
|
||||
export default {
|
||||
routerJump(routerJumpVo: RouterJumpVo, proxy) {
|
||||
console.log(routerJumpVo.formPath);
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
|
||||
proxy.$router.push({
|
||||
path: routerJumpVo.formPath,
|
||||
path: routerJumpVo.formPath ? '/' + routerJumpVo.formPath : routerJumpVo.formPath,
|
||||
query: {
|
||||
id: routerJumpVo.businessId,
|
||||
type: routerJumpVo.type,
|
||||
|
12
src/main.ts
12
src/main.ts
@ -49,12 +49,12 @@ VXETable.config({
|
||||
|
||||
//本地保存飞机配置
|
||||
import { setLocal } from './utils';
|
||||
setLocal('dockAir', 'http://192.168.110.24:9136');
|
||||
setLocal('aiUrl', 'http://192.168.110.23:8000');
|
||||
setLocal('host', '192.168.110.199');
|
||||
setLocal('rtmpPort', '1935');
|
||||
setLocal('rtcPort', '1985');
|
||||
setLocal('dockSocketUrl', 'ws://192.168.110.8:9136/websocket');
|
||||
setLocal('dockAir', 'http://58.17.134.85:9512');
|
||||
setLocal('aiUrl', 'http://58.17.134.85:9512');
|
||||
setLocal('host', '121.37.237.116');
|
||||
setLocal('rtmpPort', '28451');
|
||||
setLocal('rtcPort', '28453');
|
||||
setLocal('dockSocketUrl', 'ws://58.17.134.85:9512/websocket');
|
||||
|
||||
// 修改 el-dialog 默认点击遮照为不关闭
|
||||
/*import { ElDialog } from 'element-plus';
|
||||
|
@ -7,39 +7,23 @@ import { Style, Stroke, Fill } from 'ol/style';
|
||||
import GeoJSON from 'ol/format/GeoJSON';
|
||||
import { polygon as turfPolygon, booleanIntersects } from '@turf/turf';
|
||||
import { toLonLat } from 'ol/proj';
|
||||
import DragPan from 'ol/interaction/DragPan';
|
||||
import MouseWheelZoom from 'ol/interaction/MouseWheelZoom';
|
||||
|
||||
export class LassoSelector {
|
||||
private map: OLMap;
|
||||
private drawLayer: VectorLayer<VectorSource>;
|
||||
private drawSource: VectorSource;
|
||||
private overlaySource: VectorSource;
|
||||
private overlaySource: VectorSource; // 新增用于闭合多边形
|
||||
private overlayLayer: VectorLayer<VectorSource>;
|
||||
private drawing = false;
|
||||
private coordinates: [number, number][] = [];
|
||||
private targetSource: VectorSource;
|
||||
private isShiftKeyDown = false;
|
||||
private onSelectCallback: (selected: Feature[], isInvert?: boolean) => void;
|
||||
private dragPanInteraction: DragPan | null = null;
|
||||
private mouseWheelZoomInteraction: MouseWheelZoom | null = null;
|
||||
private onSelectCallback: (selected: Feature[]) => void;
|
||||
|
||||
constructor(map: OLMap, targetSource: VectorSource, onSelect: (selected: Feature[], isInvert?: boolean) => void) {
|
||||
constructor(map: OLMap, targetSource: VectorSource, onSelect: (selected: Feature[]) => void) {
|
||||
this.map = map;
|
||||
this.targetSource = targetSource;
|
||||
this.onSelectCallback = onSelect;
|
||||
|
||||
// 找出拖动和滚轮缩放交互
|
||||
this.dragPanInteraction = this.map
|
||||
.getInteractions()
|
||||
.getArray()
|
||||
.find((interaction) => interaction instanceof DragPan) as DragPan;
|
||||
|
||||
this.mouseWheelZoomInteraction = this.map
|
||||
.getInteractions()
|
||||
.getArray()
|
||||
.find((interaction) => interaction instanceof MouseWheelZoom) as MouseWheelZoom;
|
||||
|
||||
this.drawSource = new VectorSource();
|
||||
this.drawLayer = new VectorLayer({
|
||||
source: this.drawSource,
|
||||
@ -52,6 +36,7 @@ export class LassoSelector {
|
||||
});
|
||||
this.map.addLayer(this.drawLayer);
|
||||
|
||||
// 新增:闭合多边形图层(半透明填充)
|
||||
this.overlaySource = new VectorSource();
|
||||
this.overlayLayer = new VectorLayer({
|
||||
source: this.overlaySource,
|
||||
@ -74,25 +59,17 @@ export class LassoSelector {
|
||||
// 禁用默认右键菜单
|
||||
this.map.getViewport().addEventListener('contextmenu', (e) => e.preventDefault());
|
||||
|
||||
// pointerdown 捕获左键按下
|
||||
this.map.getViewport().addEventListener('pointerdown', (e) => {
|
||||
if (e.button === 0 && !this.drawing) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.isShiftKeyDown = e.shiftKey;
|
||||
// 右键按下开始绘制
|
||||
this.map.getViewport().addEventListener('mousedown', (e) => {
|
||||
if (e.button === 2 && !this.drawing) {
|
||||
this.drawing = true;
|
||||
this.coordinates = [];
|
||||
this.drawSource.clear();
|
||||
this.overlaySource.clear();
|
||||
|
||||
// 禁用拖动和缩放
|
||||
if (this.dragPanInteraction) this.dragPanInteraction.setActive(false);
|
||||
if (this.mouseWheelZoomInteraction) this.mouseWheelZoomInteraction.setActive(false);
|
||||
}
|
||||
});
|
||||
|
||||
// pointermove 画线
|
||||
// 鼠标移动实时绘制线和闭合多边形
|
||||
this.map.on('pointermove', (evt) => {
|
||||
if (!this.drawing) return;
|
||||
const coord = evt.coordinate as [number, number];
|
||||
@ -101,30 +78,11 @@ export class LassoSelector {
|
||||
this.renderPolygon();
|
||||
});
|
||||
|
||||
// pointerup 捕获左键抬起
|
||||
this.map.getViewport().addEventListener('pointerup', (e) => {
|
||||
if (e.button === 0 && this.drawing) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// 右键抬起结束绘制并选中
|
||||
this.map.getViewport().addEventListener('mouseup', (e) => {
|
||||
if (e.button === 2 && this.drawing) {
|
||||
this.drawing = false;
|
||||
this.handleDrawEnd();
|
||||
|
||||
// 恢复拖动和缩放
|
||||
if (this.dragPanInteraction) this.dragPanInteraction.setActive(true);
|
||||
if (this.mouseWheelZoomInteraction) this.mouseWheelZoomInteraction.setActive(true);
|
||||
}
|
||||
});
|
||||
|
||||
// 防止拖动导致意外事件
|
||||
this.map.getViewport().addEventListener('pointercancel', (e) => {
|
||||
if (this.drawing) {
|
||||
this.drawing = false;
|
||||
this.drawSource.clear();
|
||||
this.overlaySource.clear();
|
||||
|
||||
if (this.dragPanInteraction) this.dragPanInteraction.setActive(true);
|
||||
if (this.mouseWheelZoomInteraction) this.mouseWheelZoomInteraction.setActive(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -142,6 +100,7 @@ export class LassoSelector {
|
||||
this.overlaySource.clear();
|
||||
if (this.coordinates.length < 3) return;
|
||||
|
||||
// 闭合多边形坐标(首尾闭合)
|
||||
const polygonCoords = [...this.coordinates, this.coordinates[0]];
|
||||
const polygon = new Polygon([polygonCoords]);
|
||||
const feature = new Feature({ geometry: polygon });
|
||||
@ -181,10 +140,7 @@ export class LassoSelector {
|
||||
}
|
||||
});
|
||||
|
||||
if (selected.length) {
|
||||
this.onSelectCallback(selected, this.isShiftKeyDown);
|
||||
}
|
||||
|
||||
this.onSelectCallback(selected);
|
||||
this.drawSource.clear();
|
||||
this.overlaySource.clear();
|
||||
}
|
||||
@ -192,5 +148,6 @@ export class LassoSelector {
|
||||
destroy() {
|
||||
this.map.removeLayer(this.drawLayer);
|
||||
this.map.removeLayer(this.overlayLayer);
|
||||
// 如有需要,解绑事件
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export const globalHeaders = () => {
|
||||
|
||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
|
||||
axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID;
|
||||
axios.defaults.headers['projectId'] = cache.local.getJSON('selectedProject').id;
|
||||
axios.defaults.headers['isEncrypt'] = true;
|
||||
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
@ -85,6 +85,8 @@ service.interceptors.request.use(
|
||||
// 生成一个 AES 密钥
|
||||
const aesKey = generateAesKey();
|
||||
config.headers[encryptHeader] = encrypt(encryptBase64(aesKey));
|
||||
console.log(encrypt(encryptBase64(aesKey)));
|
||||
|
||||
config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
|
||||
}
|
||||
}
|
||||
|
@ -163,12 +163,11 @@ const handleSelectionChange = (selection: any) => {
|
||||
};
|
||||
|
||||
const handleDetail = async (row) => {
|
||||
const res = await listByIds(row.attachments);
|
||||
const res = await listByIds(row.id);
|
||||
tableDetail.value = {
|
||||
...row,
|
||||
...res.data[0]
|
||||
};
|
||||
console.log('🚀 ~ handleDetail ~ tableDetail.value:', tableDetail.value);
|
||||
detailVisible.value = true;
|
||||
};
|
||||
|
||||
|
@ -12,65 +12,24 @@
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="bookName" label="卷册名称" />
|
||||
<el-table-column align="center" prop="bookNo" label="卷册号" />
|
||||
<!-- <el-table-column align="center" label="附图">
|
||||
<template #default="{ row }">
|
||||
<ImagePreview :src="row.hasAttachment" disabled :width="80" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<!-- <el-table-column align="center" prop="changeReasons" label="变更原因" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.changeReasons?.join(', ') }}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column align="center" prop="changeContent" label="变更内容" />
|
||||
<el-table-column align="center" prop="costEstimate" label="变更费用估算" />
|
||||
|
||||
<!-- <el-table-column label="施工承包单位" align="center">
|
||||
<el-table-column align="center" prop="contractorManager" label="项目经理" width="100" />
|
||||
<el-table-column align="center" prop="contractorDate" label="日期" width="130">
|
||||
<template #default="{ row }">{{ formatDate(row.contractorDate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="总承包单位" align="center">
|
||||
<el-table-column align="center" prop="generalTechLeader" label="项目技术负责人" width="150" />
|
||||
<el-table-column align="center" prop="generalDate" label="日期" width="130">
|
||||
<template #default="{ row }">{{ formatDate(row.generalDate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="设计单位" align="center">
|
||||
<el-table-column align="center" prop="designer" label="设计代表" width="100" />
|
||||
<el-table-column align="center" prop="designerDate" label="日期" width="130">
|
||||
<template #default="{ row }">{{ formatDate(row.designerDate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="项目监理单位" align="center">
|
||||
<el-table-column align="center" prop="supervisorEngineer" label="监理工程师" width="100" />
|
||||
<el-table-column align="center" prop="chiefSupervisor" label="总监理工程师" width="110" />
|
||||
<el-table-column align="center" prop="supervisorDate" label="日期" width="130">
|
||||
<template #default="{ row }">{{ formatDate(row.supervisorDate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="建设单位" align="center">
|
||||
<el-table-column align="center" prop="ownerLeader" label="负责人" />
|
||||
<el-table-column align="center" prop="ownerDate" label="日期" width="130">
|
||||
<template #default="{ row }">{{ formatDate(row.ownerDate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
|
||||
<el-table-column align="center" prop="content" label="操作" width="160">
|
||||
<el-table-column align="center" prop="costEstimate" label="变更费用估算1" />
|
||||
<el-table-column label="流程状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="wf_business_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" prop="content" label="操作" width="240">
|
||||
<template #default="scope">
|
||||
<el-button link type="warning" v-if="scope.row.status === 'draft'" icon="Edit" @click="handleUpdate(scope.row)" class="ml-3"
|
||||
>审批
|
||||
</el-button>
|
||||
<el-button link type="primary" icon="View" @click="handleViewInfo(scope.row)" class="ml-3"> 查看流程 </el-button>
|
||||
<el-button link type="success" icon="View" @click="handleDetail(scope.row)" class="ml-3"> 详情 </el-button>
|
||||
<!-- <el-button link type="primary" icon="Download" @click="handleDownload()"> 下载 </el-button> -->
|
||||
<!-- <el-button link type="warning" icon="Edit" @click="handleDelete(scope.row)"> 修改 </el-button> -->
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"> 删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 详情 -->
|
||||
<el-dialog title="变更单详情" v-model="detailVisible" width="1000">
|
||||
<div class="w[850px] ma word-export-wrapper" ref="exportRef">
|
||||
@ -189,36 +148,16 @@
|
||||
import { listByIds } from '@/api/system/oss';
|
||||
import { dayjs } from 'element-plus';
|
||||
import { saveAs } from 'file-saver';
|
||||
const form = reactive({
|
||||
projectName: '',
|
||||
unitName: '',
|
||||
profession: '',
|
||||
applyDate: '2025-6-23 13:03:56',
|
||||
bookName: '',
|
||||
bookNo: '',
|
||||
hasAttachment:
|
||||
'http://58.17.134.85:9000/xinnengyuan-dev/doc/safety/knowledge/1897160897167638529/知识库/指导手册/2025-06-27_2f56bca1c4bc46c6b226858a18713c48.jpg', // 附图链接或图片地址
|
||||
changeReasons: [0, 3, 5],
|
||||
changeContent: '',
|
||||
costEstimate: '',
|
||||
|
||||
contractorManager: '',
|
||||
contractorDate: '',
|
||||
generalTechLeader: '',
|
||||
generalDate: '',
|
||||
designer: '',
|
||||
designerDate: '',
|
||||
supervisorEngineer: '',
|
||||
chiefSupervisor: '',
|
||||
supervisorDate: '',
|
||||
ownerLeader: '',
|
||||
ownerDate: ''
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
thumbnail: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
const tableDetail = ref<any>({});
|
||||
@ -237,13 +176,11 @@ const detailVisible = ref(false);
|
||||
const formatDate = (val: string | Date) => (val ? dayjs(val).format('YYYY-MM-DD') : '');
|
||||
|
||||
const handleDetail = async (row) => {
|
||||
const res = await listByIds(row.hasAttachment);
|
||||
const res = await listByIds(row.id);
|
||||
tableDetail.value = {
|
||||
...row,
|
||||
hasAttachmentList: res.data
|
||||
};
|
||||
console.log(tableDetail.value);
|
||||
|
||||
detailVisible.value = true;
|
||||
};
|
||||
/** 多选框选中数据 */
|
||||
@ -254,7 +191,32 @@ const handleSelectionChange = (selection: any) => {
|
||||
const handleDelete = (row) => {
|
||||
emit('delete', row.id);
|
||||
};
|
||||
|
||||
const handleUpdate = (row) => {
|
||||
// 添加审批
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/cory/template/indexEdit`,
|
||||
query: {
|
||||
thumbnailUrl: props.thumbnail,
|
||||
row: JSON.stringify(row),
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleViewInfo = (row) => {
|
||||
// 添加审批
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/cory/template/indexEdit`,
|
||||
query: {
|
||||
thumbnailUrl: props.thumbnail,
|
||||
row: JSON.stringify(row),
|
||||
id: row.id,
|
||||
type: 'view'
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleDownload = () => {
|
||||
const style = `
|
||||
<style>
|
||||
|
@ -1,75 +1,59 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="模板类型" prop="projectType">
|
||||
<el-select v-model="queryParams.projectType" placeholder="请选择模板类型" clearable filterable @change="selectType">
|
||||
<el-option v-for="item in projectTypeOptions" :key="item.name" :label="item.name" :value="item.name"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" :disabled="addSingle" @click="handleAdd" v-hasPermi="['quality:qualityInspection:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
</el-col>
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['quality:qualityInspection:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['quality:qualityInspection:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['quality:qualityInspection:export']">导出</el-button>
|
||||
</el-col> -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<!-- card body -->
|
||||
<Contactform
|
||||
v-if="queryParams.projectType == '外部联系单'"
|
||||
@selection-change="handleSelectionChange"
|
||||
:data="tableData"
|
||||
@delete="handleDelete"
|
||||
></Contactform>
|
||||
<EngineeringChangeApplicationForm
|
||||
v-if="queryParams.projectType == '变更单'"
|
||||
@selection-change="handleSelectionChange"
|
||||
:data="tableData"
|
||||
@delete="handleDelete"
|
||||
></EngineeringChangeApplicationForm>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<el-tabs type="border-card" v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="变更单" name="1"
|
||||
><el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" :disabled="addSingle" @click="handleAddApp" v-hasPermi="['quality:qualityInspection:add']"
|
||||
>上传变更单模版</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<EngineeringChangeApplicationForm
|
||||
@selection-change="handleSelectionChange"
|
||||
:data="tableData"
|
||||
:thumbnail="projectTypeOptions[1].thumbnail"
|
||||
@delete="handleDelete"
|
||||
></EngineeringChangeApplicationForm>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/> </el-card
|
||||
></el-tab-pane>
|
||||
<el-tab-pane label="外部联系单" name="0"
|
||||
><el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" :disabled="addSingle" @click="handleAdd" v-hasPermi="['quality:qualityInspection:add']"
|
||||
>上传外部联系单模版</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
<Contactform @selection-change="handleSelectionChange" :data="tableData" @delete="handleDelete"></Contactform>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/> </el-card
|
||||
></el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-dialog title="新增模板" v-model="dialogVisible" width="800">
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="110px">
|
||||
<div class="flex">
|
||||
<!-- <img :src="thumbnailUrl" alt="" style="width: 150px;" /> -->
|
||||
<div><image-preview :src="thumbnailUrl" width="150px"></image-preview></div>
|
||||
|
||||
<div v-if="queryParams.projectType == '外部联系单'">
|
||||
<div>
|
||||
<el-form-item label="工程名称" prop="projectName">
|
||||
<el-input v-model="form.projectName" placeholder="请输入工程名称" />
|
||||
</el-form-item>
|
||||
@ -111,94 +95,6 @@
|
||||
<el-date-picker v-model="form.ownerDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-if="queryParams.projectType === '变更单'">
|
||||
<el-form-item label="工程名称">
|
||||
<el-input v-model="form.projectName" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="提出单位">
|
||||
<el-input v-model="form.unitName" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="专业">
|
||||
<el-input v-model="form.profession" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="提出日期">
|
||||
<el-date-picker v-model="form.applyDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="卷册名称">
|
||||
<el-input v-model="form.bookName" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="卷册号">
|
||||
<el-input v-model="form.bookNo" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="附图">
|
||||
<image-upload v-model="form.hasAttachment"></image-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="变更原因">
|
||||
<el-checkbox-group v-model="form.changeReasons">
|
||||
<el-checkbox label="设计漏项" class="w45%" />
|
||||
<el-checkbox label="设计改进" />
|
||||
<el-checkbox label="设计差错" class="w45%" />
|
||||
<el-checkbox label="接口差错" />
|
||||
<el-checkbox label="业主要求" class="w45%" />
|
||||
<el-checkbox label="施工承包商要求" />
|
||||
<el-checkbox label="外部资料不符" class="w45%" />
|
||||
<el-checkbox label="材料代用或其他" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="变更内容">
|
||||
<el-input v-model="form.changeContent" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="变更费用估算">
|
||||
<el-input v-model="form.costEstimate" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">施工承包单位</el-divider>
|
||||
<el-form-item label="项目经理" prop="asupervisorLeader">
|
||||
<el-input v-model="form.asupervisorLeader" placeholder="请输入项目经理姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="asupervisorDate">
|
||||
<el-date-picker v-model="form.asupervisorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">总承包单位</el-divider>
|
||||
<el-form-item label="项目技术负责人" prop="bsupervisorLeader">
|
||||
<el-input v-model="form.bsupervisorLeader" placeholder="请输入项目技术负责人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="bsupervisorDate">
|
||||
<el-date-picker v-model="form.bsupervisorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">设计单位</el-divider>
|
||||
<el-form-item label="设计代表" prop="csupervisorLeader">
|
||||
<el-input v-model="form.csupervisorLeader" placeholder="请输入设计代表姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="csupervisorDate">
|
||||
<el-date-picker v-model="form.csupervisorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">项目监理单位</el-divider>
|
||||
<el-form-item label="监理工程师" prop="dsupervisorLeader">
|
||||
<el-input v-model="form.dsupervisorLeader" placeholder="请输入监理工程师姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="总监理工程师" prop="dasupervisorLeader">
|
||||
<el-input v-model="form.dasupervisorLeader" placeholder="请输入总监理工程师姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="dsupervisorDate">
|
||||
<el-date-picker v-model="form.dsupervisorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">建设单位</el-divider>
|
||||
<el-form-item label="负责人" prop="esupervisorLeader">
|
||||
<el-input v-model="form.esupervisorLeader" placeholder="请输入负责人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="esupervisorDate">
|
||||
<el-date-picker v-model="form.esupervisorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -224,15 +120,10 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const { safety_inspection_violation_type, safety_inspection_check_type } = toRefs<any>(
|
||||
proxy?.useDict('safety_inspection_violation_type', 'safety_inspection_check_type')
|
||||
);
|
||||
const teamOpt = ref([]);
|
||||
const foremanOpt = ref([]);
|
||||
const thumbnailUrl = ref('');
|
||||
const tableData = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const activeName = ref('1');
|
||||
const formRef = ref<FormInstance>();
|
||||
const dialogVisible = ref<boolean>(false);
|
||||
const showSearch = ref(true);
|
||||
@ -338,17 +229,6 @@ const handleAdd = () => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
const changeForeman = (value: string | number) => {
|
||||
// const team = teamList.value.filter((team) => team.id === value)[0];
|
||||
// foremanOpt.value = team.foremanList?.map((foreman: foremanQuery) => ({
|
||||
// label: foreman.foremanName,
|
||||
// value: foreman.foremanId
|
||||
// }));
|
||||
// form.value.correctorId = '';
|
||||
};
|
||||
|
||||
const handleQuery = () => {};
|
||||
const resetQuery = () => {};
|
||||
const getList = async () => {
|
||||
if (!queryParams.value.projectType) {
|
||||
const res = await listContactTypeformtemplate(queryParams.value);
|
||||
@ -376,8 +256,6 @@ const handleDelete = async (id?: string) => {
|
||||
getList();
|
||||
}
|
||||
};
|
||||
const handleUpdate = () => {};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: any) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
@ -392,10 +270,23 @@ const selectType = (value: string) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields();
|
||||
const handleClick = (val) => {
|
||||
console.log(val);
|
||||
queryParams.value.projectType = val.props.name;
|
||||
getList();
|
||||
};
|
||||
const handleAddApp = (row) => {
|
||||
// 添加审批
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/relation-management/changeContact/indexEdit`,
|
||||
query: {
|
||||
thumbnailUrl: projectTypeOptions.value[1].thumbnail,
|
||||
row,
|
||||
type: 'add'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
442
src/views/cory/template/indexEdit.vue
Normal file
442
src/views/cory/template/indexEdit.vue
Normal file
@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<div class="p-4 bg-gray-50">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<!-- 顶部按钮区域 -->
|
||||
<el-card class="mb-4 rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md">
|
||||
<approvalButton
|
||||
@submitForm="submitForm"
|
||||
@approvalVerifyOpen="approvalVerifyOpen"
|
||||
@handleApprovalRecord="handleApprovalRecord"
|
||||
:buttonLoading="buttonLoading"
|
||||
:id="form.id"
|
||||
:status="form.status"
|
||||
:pageType="routeParams.type"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
|
||||
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
|
||||
<h3 class="text-lg font-semibold text-gray-800">变更联系单</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<el-form
|
||||
ref="leaveFormRef"
|
||||
v-loading="loading"
|
||||
:disabled="routeParams.type === 'view'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
class="space-y-4"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<div class="flex">
|
||||
<div><image-preview :src="thumbnailUrl" width="150px"></image-preview></div>
|
||||
<div>
|
||||
<el-form-item label="工程名称" prop="projectName">
|
||||
<el-input v-model="form.projectName" placeholder="请输入工程名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="编号" prop="serialNumber">
|
||||
<el-input v-model="form.serialNumber" placeholder="请输入编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="致" prop="to">
|
||||
<el-input v-model="form.to" placeholder="致:" />
|
||||
</el-form-item>
|
||||
<el-form-item label="主题" prop="subject">
|
||||
<el-input v-model="form.subject" placeholder="请输入主题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="内容" prop="content">
|
||||
<el-input v-model="form.content" type="textarea" :rows="6" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" prop="attachments">
|
||||
<file-upload v-model="form.attachments" :limit="1" :file-type="['pdf', 'png', 'jpg', 'jpeg', 'gif', 'bmp']"></file-upload>
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">施工项目部</el-divider>
|
||||
<el-form-item label="项目负责人" prop="contractorLeader">
|
||||
<el-input v-model="form.contractorLeader" placeholder="请输入负责人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="contractorDate">
|
||||
<el-date-picker v-model="form.contractorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">项目监理机构</el-divider>
|
||||
<el-form-item label="总监理工程师" prop="supervisorLeader">
|
||||
<el-input v-model="form.supervisorLeader" placeholder="请输入总监理工程师姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="supervisorDate">
|
||||
<el-date-picker v-model="form.supervisorDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-divider class="mb-10! mt-10!">建设单位</el-divider>
|
||||
<el-form-item label="业主代表" prop="ownerRep">
|
||||
<el-input v-model="form.ownerRep" placeholder="请输入业主代表" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期" prop="ownerDate">
|
||||
<el-date-picker v-model="form.ownerDate" type="date" placeholder="选择日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- 提交组件 -->
|
||||
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
||||
<approvalRecord ref="approvalRecordRef"></approvalRecord>
|
||||
<!-- 流程选择对话框 -->
|
||||
<el-dialog
|
||||
draggable
|
||||
v-model="dialogVisible.visible"
|
||||
:title="dialogVisible.title"
|
||||
:before-close="handleClose"
|
||||
width="500"
|
||||
class="rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="p-4">
|
||||
<p class="text-gray-600 mb-4">请选择要启动的流程:</p>
|
||||
<el-select v-model="flowCode" placeholder="请选择流程" style="width: 100%">
|
||||
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer p-4 border-t border-gray-100 flex justify-end space-x-3">
|
||||
<el-button @click="handleClose" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button type="primary" @click="submitFlow()" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Leave" lang="ts">
|
||||
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
|
||||
import { startWorkFlow } from '@/api/workflow/task';
|
||||
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
||||
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
||||
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
||||
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { listByIds } from '@/api/system/oss';
|
||||
import { addContactnotice,getContactnotice ,updateContactnotice} from '@/api/cory/contactnotice';
|
||||
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const thumbnailUrl = ref('');
|
||||
//路由参数
|
||||
const routeParams = ref<Record<string, any>>({});
|
||||
const flowCodeOptions = [
|
||||
{
|
||||
value: currentProject.value?.id + '_changecontact',
|
||||
label: '变更联系单审批'
|
||||
},
|
||||
];
|
||||
|
||||
const flowCode = ref<string>('');
|
||||
const status = ref<string>('');
|
||||
const dialogVisible = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '流程定义'
|
||||
});
|
||||
//提交组件
|
||||
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
||||
//审批记录组件
|
||||
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
const leaveFormRef = ref<ElFormInstance>();
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
isEdit: false
|
||||
});
|
||||
const submitFormData = ref<StartProcessBo>({
|
||||
businessId: '',
|
||||
flowCode: '',
|
||||
variables: {}
|
||||
});
|
||||
const taskVariables = ref<Record<string, any>>({});
|
||||
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
projectType: '',
|
||||
projectName: '',
|
||||
serialNumber: '',
|
||||
to: '',
|
||||
subject: '',
|
||||
content: '',
|
||||
attachments: '',
|
||||
contractorLeader: '',
|
||||
contractorDate: '',
|
||||
supervisorLeader: '',
|
||||
supervisorDate: '',
|
||||
ownerRep: '',
|
||||
ownerDate: '',
|
||||
unitName: '',
|
||||
profession: '',
|
||||
applyDate: '',
|
||||
bookName: '',
|
||||
bookNo: '',
|
||||
hasAttachment: '',
|
||||
changeReasons: [],
|
||||
changeContent: '',
|
||||
costEstimate: '',
|
||||
status: ''
|
||||
};
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value?.id,
|
||||
fileName: undefined,
|
||||
fileType: undefined,
|
||||
fileSuffix: undefined,
|
||||
fileStatus: undefined,
|
||||
originalName: undefined,
|
||||
newest: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
projectName: [{ required: true, message: '请输入工程名称', trigger: 'blur' }],
|
||||
projectType: [{ required: true, message: '请选择模板类型', trigger: 'blur' }],
|
||||
serialNumber: [{ required: true, message: '请输入编号', trigger: 'blur' }],
|
||||
to: [{ required: true, message: '请输入接收方', trigger: 'blur' }],
|
||||
subject: [{ required: true, message: '请输入主题', trigger: 'blur' }],
|
||||
content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.visible = false;
|
||||
flowCode.value = '';
|
||||
buttonLoading.value = false;
|
||||
};
|
||||
const { form, rules } = toRefs(data);
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
leaveFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 获取详情 */
|
||||
const getInfo = () => {
|
||||
loading.value = true;
|
||||
buttonLoading.value = false;
|
||||
nextTick(async () => {
|
||||
const res = await getContactnotice(routeParams.value.id);
|
||||
var data = {
|
||||
...JSON.parse(routeParams.value.row),
|
||||
...JSON.parse(res.data.detail)
|
||||
};
|
||||
console.log(routeParams.value);
|
||||
|
||||
Object.assign(form.value, data);
|
||||
loading.value = false;
|
||||
buttonLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = (status1: string) => {
|
||||
status.value = status1;
|
||||
leaveFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
// var res;
|
||||
// if (form.value.id) {
|
||||
// res = await updateContactnotice(form.value).finally(() => (buttonLoading.value = false));
|
||||
// } else {
|
||||
// res = await addContactnotice(form.value).finally(() => (buttonLoading.value = false));
|
||||
// }
|
||||
// if (res.code == 200) {
|
||||
dialog.visible = false;
|
||||
submit(status.value, form.value);
|
||||
// }
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submitFlow = async () => {
|
||||
handleStartWorkFlow(form.value);
|
||||
dialogVisible.visible = false;
|
||||
};
|
||||
//提交申请
|
||||
const handleStartWorkFlow = async (data: LeaveForm) => {
|
||||
try {
|
||||
submitFormData.value.flowCode = flowCode.value;
|
||||
submitFormData.value.businessId = data.id;
|
||||
//流程变量
|
||||
taskVariables.value = {
|
||||
// leave4/5 使用的流程变量
|
||||
userList: ['1', '3', '4']
|
||||
};
|
||||
submitFormData.value.variables = taskVariables.value;
|
||||
const resp = await startWorkFlow(submitFormData.value);
|
||||
if (submitVerifyRef.value) {
|
||||
buttonLoading.value = false;
|
||||
submitVerifyRef.value.openDialog(resp.data.taskId);
|
||||
}
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
};
|
||||
//审批记录
|
||||
const handleApprovalRecord = () => {
|
||||
approvalRecordRef.value.init(form.value.id);
|
||||
};
|
||||
//提交回调
|
||||
const submitCallback = async () => {
|
||||
await proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
};
|
||||
//审批
|
||||
const approvalVerifyOpen = async () => {
|
||||
submitVerifyRef.value.openDialog(routeParams.value.taskId);
|
||||
};
|
||||
// 图纸上传成功之后 开始提交
|
||||
const submit = async (status, data) => {
|
||||
form.value = data;
|
||||
if (status === 'draft') {
|
||||
buttonLoading.value = false;
|
||||
proxy?.$modal.msgSuccess('暂存成功');
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
} else {
|
||||
if ((form.value.status === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
|
||||
flowCode.value = flowCodeOptions[0].value;
|
||||
dialogVisible.visible = true;
|
||||
return;
|
||||
}
|
||||
//说明启动过先随意穿个参数
|
||||
if (flowCode.value === '' || flowCode.value === null) {
|
||||
flowCode.value = 'xx';
|
||||
}
|
||||
console.log(data);
|
||||
await handleStartWorkFlow(data);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(async () => {
|
||||
routeParams.value = proxy.$route.query;
|
||||
thumbnailUrl.value = proxy.$route.query.thumbnailUrl;
|
||||
reset();
|
||||
loading.value = false;
|
||||
if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
|
||||
getInfo();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
/* 全局样式 */
|
||||
:root {
|
||||
--primary: #409eff;
|
||||
--primary-light: #66b1ff;
|
||||
--primary-dark: #3a8ee6;
|
||||
--success: #67c23a;
|
||||
--warning: #e6a23c;
|
||||
--danger: #f56c6c;
|
||||
--info: #909399;
|
||||
}
|
||||
|
||||
/* 表单样式优化 */
|
||||
.el-form-item {
|
||||
.el-form-item__label {
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-input__inner,
|
||||
.el-select .el-input__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮样式优化 */
|
||||
.el-button {
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.is-primary {
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-light);
|
||||
border-color: var(--primary-light);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-text {
|
||||
color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-light);
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片样式优化 */
|
||||
.el-card {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
/* transform: translateY(-2px); */
|
||||
}
|
||||
}
|
||||
|
||||
/* 对话框样式优化 */
|
||||
.el-dialog {
|
||||
.el-dialog__header {
|
||||
background-color: #f5f7fa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -306,7 +306,19 @@ const selectType = (value: string) => {
|
||||
const resetForm = () => {
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
@ -16,15 +16,16 @@
|
||||
<el-form-item label="专业" prop="specialty">
|
||||
<el-input v-model="queryParams.specialty" placeholder="请输入专业" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="提出日期" prop="submitDate">
|
||||
<el-date-picker clearable v-model="queryParams.submitDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择提出日期" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="卷册名称" prop="volumeName">
|
||||
<el-input v-model="queryParams.volumeName" placeholder="请输入卷册名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="卷册号" prop="volumeNo">
|
||||
<el-input v-model="queryParams.volumeNo" placeholder="请输入卷册号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="提出日期" prop="submitDate">
|
||||
<el-date-picker clearable v-model="queryParams.submitDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择提出日期" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
@ -38,12 +39,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['design:designChange:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['design:designChange:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['design:designChange:add']">上传设计变更</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@ -68,16 +64,26 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="变更内容" align="center" prop="changeContent" />
|
||||
<!-- <el-table-column label="变更费用估算" align="center" prop="costEstimation" /> -->
|
||||
<el-table-column label="变更文件" align="center" prop="fileId">
|
||||
<el-table-column label="变更文件" align="center">
|
||||
<template #default="scope">
|
||||
<span style="color: rgb(41 145 255);cursor: pointer" @click="onOpen(scope.row.file.url)"> {{ scope.row.file.originalName }}</span>
|
||||
<span v-if="scope.row.file" style="color: rgb(41 145 255); cursor: pointer" @click="onOpen(scope.row.file.url)">
|
||||
{{ scope.row.file.originalName }}</span
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="流程状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="wf_business_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)"></el-button>
|
||||
<el-tooltip content="查看流程" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleViewInfo(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看文档" placement="top">
|
||||
<el-button link type="primary" icon="Document" @click="handleView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['design:designChange:edit']"></el-button>
|
||||
@ -90,91 +96,13 @@
|
||||
</el-table>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<el-dialog
|
||||
:title="dialog.title"
|
||||
v-model="dialog.visible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form ref="designChangeFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请单编号" prop="formNo">
|
||||
<el-input v-model="form.formNo" placeholder="请输入申请单编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="工程名称" prop="projectName"> <el-input v-model="form.projectName" placeholder="请输入工程名称" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提出单位" prop="submitUnit">
|
||||
<el-input v-model="form.submitUnit" placeholder="请输入提出单位" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="专业" prop="specialty">
|
||||
<el-input v-model="form.specialty" placeholder="请输入专业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提出日期" prop="submitDate">
|
||||
<el-date-picker clearable v-model="form.submitDate" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择提出日期">
|
||||
</el-date-picker> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="卷册名称" prop="volumeName"> <el-input v-model="form.volumeName" placeholder="请输入卷册名称" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="卷册号" prop="volumeNo"> <el-input v-model="form.volumeNo" placeholder="请输入卷册号" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="附图" prop="attachmentPic"> <image-upload v-model="form.attachmentPic" :fileSize="100" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更原因" prop="changeReason">
|
||||
<el-checkbox-group v-model="form.changeReason">
|
||||
<el-checkbox v-for="dict in design_change_reason_type" :key="dict.value" :label="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item></el-col
|
||||
>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更内容" prop="changeContent">
|
||||
<el-input v-model="form.changeContent" type="textarea" placeholder="请输入内容" /> </el-form-item
|
||||
></el-col>
|
||||
<!-- <el-col :span="12">
|
||||
<el-form-item label="变更费用估算" prop="costEstimation">
|
||||
<el-input v-model="form.costEstimation" type="textarea" placeholder="请输入内容" /> </el-form-item
|
||||
></el-col> -->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更费用估算表" label-width="110px" prop="costEstimationFile">
|
||||
<file-upload v-model="form.costEstimationFile" :fileSize="100" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更文件" prop="fileId"> <file-upload v-model="form.fileId" :fileSize="100" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24"
|
||||
><el-form-item label="备注" prop="remark"> <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> </el-form-item
|
||||
></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<wordDetial ref="wordDetialRef"></wordDetial>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="DesignChange" lang="ts">
|
||||
import { listDesignChange, getDesignChange, delDesignChange, addDesignChange, updateDesignChange } from '@/api/design/designChange';
|
||||
import { DesignChangeVO, DesignChangeQuery, DesignChangeForm } from '@/api/design/designChange/types';
|
||||
import { listDesignChange, delDesignChange } from '@/api/design/designChange';
|
||||
import { DesignChangeVO } from '@/api/design/designChange/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import wordDetial from '@/components/wordDetial/index';
|
||||
|
||||
@ -185,7 +113,6 @@ const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const designChangeList = ref<DesignChangeVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const wordDetialRef = ref<InstanceType<typeof wordDetial>>();
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
@ -193,36 +120,9 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const designChangeFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: DesignChangeForm = {
|
||||
id: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
formNo: undefined,
|
||||
projectName: undefined,
|
||||
submitUnit: undefined,
|
||||
specialty: undefined,
|
||||
submitDate: undefined,
|
||||
volumeName: undefined,
|
||||
volumeNo: undefined,
|
||||
attachmentPic: undefined,
|
||||
changeReason: [],
|
||||
changeContent: undefined,
|
||||
costEstimation: undefined,
|
||||
costEstimationFile: undefined,
|
||||
fileId: undefined,
|
||||
status: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive<PageData<DesignChangeForm, DesignChangeQuery>>({
|
||||
form: { ...initFormData },
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@ -237,14 +137,10 @@ const data = reactive<PageData<DesignChangeForm, DesignChangeQuery>>({
|
||||
changeReason: undefined,
|
||||
status: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }],
|
||||
projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
/** 查询设计变更管理列表 */
|
||||
const getList = async () => {
|
||||
@ -255,18 +151,6 @@ const getList = async () => {
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
designChangeFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
@ -288,40 +172,37 @@ const handleSelectionChange = (selection: DesignChangeVO[]) => {
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加设计变更';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: DesignChangeVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getDesignChange(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
form.value.changeReason = form.value.changeReason.split(',');
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改设计变更';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
designChangeFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
form.value.changeReason = form.value.changeReason.join(',');
|
||||
if (form.value.id) {
|
||||
await updateDesignChange({ ...form.value }).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await addDesignChange(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/design-management/designChange/indexEdit`,
|
||||
query: {
|
||||
type: 'add'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: DesignChangeVO) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/design-management/designChange/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 查看按钮操作 */
|
||||
const handleViewInfo = (row) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/design-management/designChange/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'view'
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: DesignChangeVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
@ -332,13 +213,25 @@ const handleDelete = async (row?: DesignChangeVO) => {
|
||||
};
|
||||
const handleView = (row) => {
|
||||
// 查看详情
|
||||
wordDetialRef.value?.openDialog(row,design_change_reason_type.value);
|
||||
wordDetialRef.value?.openDialog(row, design_change_reason_type.value);
|
||||
};
|
||||
// 预览
|
||||
const onOpen = (path: string) => {
|
||||
window.open(path, '_blank');
|
||||
window.open(path, '_blank');
|
||||
};
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
|
442
src/views/design/designChange/indexEdit.vue
Normal file
442
src/views/design/designChange/indexEdit.vue
Normal file
@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<div class="p-4 bg-gray-50">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<!-- 顶部按钮区域 -->
|
||||
<el-card class="mb-4 rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md">
|
||||
<approvalButton
|
||||
@submitForm="submitForm"
|
||||
@approvalVerifyOpen="approvalVerifyOpen"
|
||||
@handleApprovalRecord="handleApprovalRecord"
|
||||
:buttonLoading="buttonLoading"
|
||||
:id="form.id"
|
||||
:status="form.status"
|
||||
:pageType="routeParams.type"
|
||||
/>
|
||||
</el-card>
|
||||
<!-- 表单区域 -->
|
||||
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
|
||||
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
|
||||
<h3 class="text-lg font-semibold text-gray-800">设计变更信息</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<el-form
|
||||
ref="leaveFormRef"
|
||||
v-loading="loading"
|
||||
:disabled="routeParams.type === 'view'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
class="space-y-4"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请单编号" prop="formNo">
|
||||
<el-input v-model="form.formNo" placeholder="请输入申请单编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="工程名称" prop="projectName">
|
||||
<el-input v-model="form.projectName" placeholder="请输入工程名称" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提出单位" prop="submitUnit">
|
||||
<el-input v-model="form.submitUnit" placeholder="请输入提出单位" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="专业" prop="specialty">
|
||||
<el-input v-model="form.specialty" placeholder="请输入专业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提出日期" prop="submitDate">
|
||||
<el-date-picker clearable v-model="form.submitDate" type="date" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择提出日期">
|
||||
</el-date-picker> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="卷册名称" prop="volumeName"> <el-input v-model="form.volumeName" placeholder="请输入卷册名称" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="卷册号" prop="volumeNo"> <el-input v-model="form.volumeNo" placeholder="请输入卷册号" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="附图" prop="attachmentPic"> <image-upload v-model="form.attachmentPic" :fileSize="100" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更原因" prop="changeReason">
|
||||
<el-checkbox-group v-model="form.changeReason">
|
||||
<el-checkbox v-for="dict in design_change_reason_type" :key="dict.value" :value="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item></el-col
|
||||
>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更内容" prop="changeContent">
|
||||
<el-input v-model="form.changeContent" type="textarea" placeholder="请输入内容" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更费用估算表" label-width="110px" prop="costEstimationFile">
|
||||
<file-upload v-model="form.costEstimationFile" :fileSize="100" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="变更文件" prop="fileId"> <file-upload v-model="form.fileId" :fileSize="100" /> </el-form-item
|
||||
></el-col>
|
||||
<el-col :span="24"
|
||||
><el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> </el-form-item
|
||||
></el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- 提交组件 -->
|
||||
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
||||
<approvalRecord ref="approvalRecordRef"></approvalRecord>
|
||||
<!-- 流程选择对话框 -->
|
||||
<el-dialog
|
||||
draggable
|
||||
v-model="dialogVisible.visible"
|
||||
:title="dialogVisible.title"
|
||||
:before-close="handleClose"
|
||||
width="500"
|
||||
class="rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="p-4">
|
||||
<p class="text-gray-600 mb-4">请选择要启动的流程:</p>
|
||||
<el-select v-model="flowCode" placeholder="请选择流程" style="width: 100%">
|
||||
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer p-4 border-t border-gray-100 flex justify-end space-x-3">
|
||||
<el-button @click="handleClose" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button type="primary" @click="submitFlow()" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Leave" lang="ts">
|
||||
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
|
||||
import { startWorkFlow } from '@/api/workflow/task';
|
||||
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
||||
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
||||
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
||||
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { getDesignChange, addDesignChange, updateDesignChange } from '@/api/design/designChange';
|
||||
const { design_change_reason_type } = toRefs<any>(proxy?.useDict('design_change_reason_type'));
|
||||
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
//路由参数
|
||||
const routeParams = ref<Record<string, any>>({});
|
||||
const flowCodeOptions = [
|
||||
{
|
||||
value: currentProject.value?.id + '_designchanged',
|
||||
label: '设计变更审批'
|
||||
},
|
||||
];
|
||||
|
||||
const flowCode = ref<string>('');
|
||||
const status = ref<string>('');
|
||||
const dialogVisible = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '流程定义'
|
||||
});
|
||||
//提交组件
|
||||
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
||||
//审批记录组件
|
||||
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
//按钮组件
|
||||
const approvalButtonRef = ref<InstanceType<typeof ApprovalButton>>();
|
||||
|
||||
const leaveFormRef = ref<ElFormInstance>();
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
isEdit: false
|
||||
});
|
||||
const submitFormData = ref<StartProcessBo>({
|
||||
businessId: '',
|
||||
flowCode: '',
|
||||
variables: {}
|
||||
});
|
||||
const taskVariables = ref<Record<string, any>>({});
|
||||
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
formNo: undefined,
|
||||
projectName: undefined,
|
||||
submitUnit: undefined,
|
||||
specialty: undefined,
|
||||
submitDate: undefined,
|
||||
volumeName: undefined,
|
||||
volumeNo: undefined,
|
||||
attachmentPic: undefined,
|
||||
changeReason: [],
|
||||
changeContent: undefined,
|
||||
costEstimation: undefined,
|
||||
costEstimationFile: undefined,
|
||||
fileId: undefined,
|
||||
status: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value?.id,
|
||||
fileName: undefined,
|
||||
fileType: undefined,
|
||||
fileSuffix: undefined,
|
||||
fileStatus: undefined,
|
||||
originalName: undefined,
|
||||
newest: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {}
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.visible = false;
|
||||
flowCode.value = '';
|
||||
buttonLoading.value = false;
|
||||
};
|
||||
const { form, rules } = toRefs(data);
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
leaveFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 获取详情 */
|
||||
const getInfo = () => {
|
||||
loading.value = true;
|
||||
buttonLoading.value = false;
|
||||
nextTick(async () => {
|
||||
const res = await getDesignChange(routeParams.value.id);
|
||||
Object.assign(form.value, res.data);
|
||||
if(form.value.changeReason.length >0){
|
||||
form.value.changeReason = form.value.changeReason.split(',');
|
||||
}
|
||||
loading.value = false;
|
||||
buttonLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = (status1: string) => {
|
||||
status.value = status1;
|
||||
var changeReason=''
|
||||
if(form.value.changeReason.length >0){
|
||||
changeReason = form.value.changeReason.join(',');
|
||||
}
|
||||
leaveFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
var res;
|
||||
if (form.value.id) {
|
||||
res = await updateDesignChange({...form.value,changeReason}).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
res = await addDesignChange({...form.value,changeReason}).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
if (res.code == 200) {
|
||||
dialog.visible = false;
|
||||
submit(status.value, res.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const submitFlow = async () => {
|
||||
handleStartWorkFlow(form.value);
|
||||
dialogVisible.visible = false;
|
||||
};
|
||||
//提交申请
|
||||
const handleStartWorkFlow = async (data: LeaveForm) => {
|
||||
try {
|
||||
submitFormData.value.flowCode = flowCode.value;
|
||||
submitFormData.value.businessId = data.id;
|
||||
//流程变量
|
||||
taskVariables.value = {
|
||||
// leave4/5 使用的流程变量
|
||||
userList: ['1', '3', '4']
|
||||
};
|
||||
submitFormData.value.variables = taskVariables.value;
|
||||
const resp = await startWorkFlow(submitFormData.value);
|
||||
if (submitVerifyRef.value) {
|
||||
buttonLoading.value = false;
|
||||
submitVerifyRef.value.openDialog(resp.data.taskId);
|
||||
}
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
};
|
||||
//审批记录
|
||||
const handleApprovalRecord = () => {
|
||||
approvalRecordRef.value.init(form.value.id);
|
||||
};
|
||||
//提交回调
|
||||
const submitCallback = async () => {
|
||||
await proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
};
|
||||
//审批
|
||||
const approvalVerifyOpen = async () => {
|
||||
submitVerifyRef.value.openDialog(routeParams.value.taskId);
|
||||
};
|
||||
// 图纸上传成功之后 开始提交
|
||||
const submit = async (status, data) => {
|
||||
form.value = data;
|
||||
if (status === 'draft') {
|
||||
buttonLoading.value = false;
|
||||
proxy?.$modal.msgSuccess('暂存成功');
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
} else {
|
||||
if ((form.value.status === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
|
||||
flowCode.value = flowCodeOptions[0].value;
|
||||
dialogVisible.visible = true;
|
||||
return;
|
||||
}
|
||||
//说明启动过先随意穿个参数
|
||||
if (flowCode.value === '' || flowCode.value === null) {
|
||||
flowCode.value = 'xx';
|
||||
}
|
||||
await handleStartWorkFlow(data);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(async () => {
|
||||
routeParams.value = proxy.$route.query;
|
||||
reset();
|
||||
loading.value = false;
|
||||
if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
|
||||
getInfo();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
/* 全局样式 */
|
||||
:root {
|
||||
--primary: #409eff;
|
||||
--primary-light: #66b1ff;
|
||||
--primary-dark: #3a8ee6;
|
||||
--success: #67c23a;
|
||||
--warning: #e6a23c;
|
||||
--danger: #f56c6c;
|
||||
--info: #909399;
|
||||
}
|
||||
|
||||
/* 表单样式优化 */
|
||||
.el-form-item {
|
||||
.el-form-item__label {
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-input__inner,
|
||||
.el-select .el-input__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮样式优化 */
|
||||
.el-button {
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.is-primary {
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-light);
|
||||
border-color: var(--primary-light);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-text {
|
||||
color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-light);
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片样式优化 */
|
||||
.el-card {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
/* transform: translateY(-2px); */
|
||||
}
|
||||
}
|
||||
|
||||
/* 对话框样式优化 */
|
||||
.el-dialog {
|
||||
.el-dialog__header {
|
||||
background-color: #f5f7fa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -88,8 +88,8 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Drawing" lang="ts">
|
||||
import { listDrawing, getDrawing, delDrawing, addDrawing, updateDrawing } from '@/api/design/drawing';
|
||||
import { DrawingVO, DrawingQuery, DrawingForm } from '@/api/design/drawing/types';
|
||||
import { listDrawing, delDrawing } from '@/api/design/drawing';
|
||||
import { DrawingVO } from '@/api/design/drawing/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { LeaveVO } from '@/api/workflow/leave/types';
|
||||
import { cancelProcessApply } from '@/api/workflow/instance';
|
||||
@ -110,65 +110,24 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const fileList = ref([]); // 存储上传的文件列表
|
||||
const selectedFile = ref(null); // 当前选中的文件
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const drawingFormRef = ref<ElFormInstance>();
|
||||
const activeName = ref('1');
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
isEdit: false
|
||||
});
|
||||
const initFormData: DrawingForm = {
|
||||
id: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
versionNumber: undefined,
|
||||
fileName: undefined,
|
||||
fileUrl: undefined,
|
||||
fileType: undefined,
|
||||
fileSuffix: undefined,
|
||||
originalName: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive<PageData<DrawingForm, DrawingQuery>>({
|
||||
form: { ...initFormData },
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value?.id,
|
||||
fileName: undefined,
|
||||
fileType: undefined,
|
||||
fileType: 1,
|
||||
fileSuffix: undefined,
|
||||
fileStatus: undefined,
|
||||
originalName: undefined,
|
||||
newest: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
versionNumber: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
|
||||
fileName: [{ required: true, message: '文件名称不能为空', trigger: 'blur' }],
|
||||
fileType: [{ required: true, message: '文件类型不能为空', trigger: 'change' }],
|
||||
file: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
console.log(dialog.isEdit);
|
||||
console.log(fileList.value);
|
||||
|
||||
// 新增时必须上传文件
|
||||
if (!dialog.isEdit && !fileList.value.length) {
|
||||
callback(new Error('请上传图纸文件'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
/** 查询图纸管理列表 */
|
||||
const getList = async () => {
|
||||
@ -179,20 +138,6 @@ const getList = async () => {
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
drawingFormRef.value?.resetFields();
|
||||
fileList.value = [];
|
||||
selectedFile.value = null;
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
@ -216,7 +161,7 @@ const handleSelectionChange = (selection: DrawingVO[]) => {
|
||||
const handleAdd = () => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/drawing/indexEdit`,
|
||||
path: `/design-management/drawing/indexEdit`,
|
||||
query: {
|
||||
type: 'add'
|
||||
}
|
||||
@ -227,7 +172,7 @@ const handleAdd = () => {
|
||||
const handleUpdate = async (row?: DrawingVO) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/drawing/indexEdit`,
|
||||
path: `/design-management/drawing/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
@ -253,7 +198,7 @@ const handleView = (row) => {
|
||||
const handleViewInfo = (row?: LeaveVO) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/drawing/indexEdit`,
|
||||
path: `/design-management/drawing/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'view'
|
||||
@ -283,4 +228,16 @@ const handleClick = (val) => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
@ -176,7 +176,7 @@ const data = reactive<PageData<LeaveForm, LeaveQuery>>({
|
||||
versionNumber: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
|
||||
fileName: [{ required: true, message: '文件名称不能为空', trigger: 'blur' }],
|
||||
fileType: [{ required: true, message: '文件类型不能为空', trigger: 'change' }],
|
||||
file: [
|
||||
fileUrl: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// 新增时必须上传文件
|
||||
|
@ -14,8 +14,8 @@
|
||||
<el-input v-model="queryParams.originalName" placeholder="请输入原文件名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审核状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择审核状态" clearable >
|
||||
<el-option v-for="dict in wf_business_status" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
<el-select v-model="queryParams.status" placeholder="请选择审核状态" clearable>
|
||||
<el-option v-for="dict in wf_business_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@ -46,12 +46,11 @@
|
||||
<el-table-column label="原文件名" align="center" prop="originalName" />
|
||||
<el-table-column label="流程状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="wf_business_status" :value="scope.row.status"/>
|
||||
<dict-tag :options="wf_business_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime" width="180"> </el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding">
|
||||
<template #default="scope">
|
||||
<el-row :gutter="10" class="mb8">
|
||||
@ -68,7 +67,9 @@
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" size="small" icon="View" v-if="scope.row.status != 'draft'" @click="handleViewInfo(scope.row)">查看</el-button>
|
||||
<el-button type="primary" size="small" icon="View" v-if="scope.row.status != 'draft'" @click="handleViewInfo(scope.row)"
|
||||
>查看</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="scope.row.status === 'waiting'">
|
||||
<el-button size="small" type="primary" icon="Notification" @click="handleCancelProcessApply(scope.row.id)">撤销</el-button>
|
||||
@ -77,15 +78,14 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SpecialScheme" lang="ts">
|
||||
import { listSpecialScheme, getSpecialScheme, delSpecialScheme, addSpecialScheme, updateSpecialScheme } from '@/api/design/specialScheme';
|
||||
import { SpecialSchemeVO, SpecialSchemeQuery, SpecialSchemeForm } from '@/api/design/specialScheme/types';
|
||||
import { listSpecialScheme, delSpecialScheme } from '@/api/design/specialScheme';
|
||||
import { SpecialSchemeVO } from '@/api/design/specialScheme/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { cancelProcessApply } from '@/api/workflow/instance';
|
||||
|
||||
@ -96,7 +96,6 @@ const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const specialSchemeList = ref<SpecialSchemeVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
@ -105,24 +104,8 @@ const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const specialSchemeFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: SpecialSchemeForm = {
|
||||
id: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
versionNumber: undefined,
|
||||
fileName: undefined,
|
||||
fileUrl: undefined,
|
||||
fileSuffix: undefined,
|
||||
remark: undefined,
|
||||
}
|
||||
const data = reactive<PageData<SpecialSchemeForm, SpecialSchemeQuery>>({
|
||||
form: {...initFormData},
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@ -131,20 +114,11 @@ const data = reactive<PageData<SpecialSchemeForm, SpecialSchemeQuery>>({
|
||||
fileName: undefined,
|
||||
originalName: undefined,
|
||||
status: undefined,
|
||||
params: {
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
versionNumber: [
|
||||
{ required: true, message: "版本号不能为空", trigger: "blur" }
|
||||
],
|
||||
fileName: [
|
||||
{ required: true, message: "文件名称不能为空", trigger: "blur" }
|
||||
],
|
||||
params: {}
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { queryParams } = toRefs(data);
|
||||
|
||||
/** 查询专项方案管理列表 */
|
||||
const getList = async () => {
|
||||
@ -153,96 +127,66 @@ const getList = async () => {
|
||||
specialSchemeList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
}
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = {...initFormData};
|
||||
specialSchemeFormRef.value?.resetFields();
|
||||
}
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: SpecialSchemeVO[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/specialScheme/indexEdit`,
|
||||
path: `/design-management/specialScheme/indexEdit`,
|
||||
query: {
|
||||
type: 'add'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: SpecialSchemeVO) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/specialScheme/indexEdit`,
|
||||
path: `/design-management/specialScheme/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
specialSchemeFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateSpecialScheme(form.value).finally(() => buttonLoading.value = false);
|
||||
} else {
|
||||
await addSpecialScheme(form.value).finally(() => buttonLoading.value = false);
|
||||
}
|
||||
proxy?.$modal.msgSuccess("操作成功");
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: SpecialSchemeVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除专项方案管理编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
|
||||
await proxy?.$modal.confirm('是否确认删除专项方案管理编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||||
await delSpecialScheme(_ids);
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
const handleView = (row) => {
|
||||
var url= row.file.url
|
||||
var url = row.file.url;
|
||||
window.open(url, '_blank');
|
||||
};
|
||||
/** 查看按钮操作 */
|
||||
const handleViewInfo = (row) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/specialScheme/indexEdit`,
|
||||
path: `/design-management/specialScheme/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'view'
|
||||
@ -264,4 +208,16 @@ const handleCancelProcessApply = async (id: string) => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
|
@ -1,143 +1,155 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeName" class="demo-tabs p5" @tab-click="handleCheckMian">
|
||||
<el-tab-pane label="资料" name="first">
|
||||
<div class="profile_engin">
|
||||
<div class="box_info">
|
||||
<div class="tree_left1" id="tree_left1">
|
||||
<div class="file_upload check_select">
|
||||
<div class="box_btn">
|
||||
<file-upload
|
||||
v-model="state.paramsQuery.file"
|
||||
:limit="100"
|
||||
:uploadUrl="uploadUrl"
|
||||
:params="uploadParams"
|
||||
:on-upload-success="uploadFile"
|
||||
:fileType="[]"
|
||||
<div>
|
||||
<el-tabs v-model="activeName" class="demo-tabs p5" @tab-click="handleCheckMian">
|
||||
<el-tab-pane label="资料" name="first">
|
||||
<div class="profile_engin">
|
||||
<div class="box_info">
|
||||
<div class="tree_left1" id="tree_left1">
|
||||
<div class="file_upload check_select">
|
||||
<div class="box_btn">
|
||||
<file-upload
|
||||
v-model="state.paramsQuery.file"
|
||||
:limit="100"
|
||||
:uploadUrl="uploadUrl"
|
||||
:params="uploadParams"
|
||||
:on-upload-success="uploadFile"
|
||||
:fileType="[]"
|
||||
>
|
||||
<el-button type="primary" style="float: left" :disabled="!state.parentPid">
|
||||
<el-icon size="small"><Plus /></el-icon>上传文件
|
||||
</el-button>
|
||||
</file-upload>
|
||||
</div>
|
||||
<el-button type="primary" :disabled="!state.parentPid" @click="onExport"
|
||||
><el-icon><Download /></el-icon>下载</el-button
|
||||
>
|
||||
<el-button type="primary" @click="onBook"
|
||||
><el-icon><View /></el-icon>查看全项目文件</el-button
|
||||
>
|
||||
<el-button type="primary" style="float: left" :disabled="!state.parentPid">
|
||||
<el-icon size="small"><Plus /></el-icon>上传文件
|
||||
</el-button>
|
||||
</file-upload>
|
||||
</div>
|
||||
<el-button type="primary" :disabled="!state.parentPid" @click="onExport"
|
||||
><el-icon><Download /></el-icon>下载</el-button
|
||||
<div class="file_upload check_select">
|
||||
<el-input class="input_left" v-model="filterText" size="small" placeholder="请输入文件名称" />
|
||||
</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
highlight-current
|
||||
:default-expand-all="state.checked"
|
||||
:filter-node-method="filterFolder"
|
||||
:data="state.treeList"
|
||||
node-key="id"
|
||||
accordion
|
||||
:expand-on-click-node="false"
|
||||
@node-click="handleNodeClick"
|
||||
:current-node-key="state.selectedNodeId"
|
||||
>
|
||||
<el-button type="primary" @click="onBook"
|
||||
><el-icon><View /></el-icon>查看全项目文件</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="file_upload check_select">
|
||||
<el-input class="input_left" v-model="filterText" size="small" placeholder="请输入文件名称" />
|
||||
</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
highlight-current
|
||||
:default-expand-all="state.checked"
|
||||
:filter-node-method="filterFolder"
|
||||
:data="state.treeList"
|
||||
node-key="id"
|
||||
accordion
|
||||
:expand-on-click-node="false"
|
||||
@node-click="handleNodeClick"
|
||||
:current-node-key="state.selectedNodeId"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<el-icon color="#f1a81a"><FolderOpened /></el-icon>
|
||||
<span>{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
<div class="resize-handle resize-handle-right right"></div>
|
||||
</div>
|
||||
<div class="list_right" id="list_right1">
|
||||
<div>
|
||||
<el-form :model="state.paramsQuery" ref="queryRef" :inline="true" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="7" class="colBlock">
|
||||
<el-form-item label="文件名称" prop="fileName">
|
||||
<el-input
|
||||
v-model="state.paramsQuery.fileName"
|
||||
placeholder="请输入文件名称"
|
||||
clearable
|
||||
@keyup.enter.native="getdocumentDataList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" class="m-l10">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="searchInfo"
|
||||
><el-icon><Search /></el-icon>搜索</el-button
|
||||
>
|
||||
<el-button @click="resetQuery"
|
||||
><el-icon><Refresh /></el-icon>重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table v-loading="state.loading" :data="state.infoList" height="67vh" border>
|
||||
<el-table-column label="序号" align="center" type="index" min-width="50px" />
|
||||
<el-table-column label="文件名称" align="center" prop="fileName"></el-table-column>
|
||||
<el-table-column label="文件类型" align="center" prop="fileSuffix" width="100px" />
|
||||
|
||||
<el-table-column label="上传时间" align="center" prop="createTime"> </el-table-column>
|
||||
<el-table-column label="操作" align="center" width="300">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link @click="handleView(scope.row)" v-if="acceptType.includes(scope.row.fileSuffix)"
|
||||
><el-icon><View /></el-icon>查看</el-button
|
||||
>
|
||||
<el-button type="primary" v-if="state.wordType.includes(scope.row.fileSuffix)" link @click="updataView(scope.row)"
|
||||
><el-icon><EditPen /></el-icon>修改文件</el-button
|
||||
>
|
||||
<el-button type="primary" link @click="onExportView(scope.row)"
|
||||
><el-icon><Download /></el-icon>下载</el-button
|
||||
>
|
||||
<el-button type="success" link @click="updateName(scope.row)"
|
||||
><el-icon><EditPen /></el-icon>修改名称</el-button
|
||||
>
|
||||
<el-button type="danger" link @click="handleDelete(scope.row)"
|
||||
><el-icon><DeleteFilled /></el-icon>删除</el-button
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<el-icon color="#f1a81a"><FolderOpened /></el-icon>
|
||||
<span>{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
:total="state.total"
|
||||
v-model:page="state.paramsQuery.pageNum"
|
||||
v-model:limit="state.paramsQuery.pageSize"
|
||||
@pagination="getdocumentDataList"
|
||||
/>
|
||||
</el-tree>
|
||||
<div class="resize-handle resize-handle-right right"></div>
|
||||
</div>
|
||||
<div class="list_right" id="list_right1">
|
||||
<div>
|
||||
<el-form :model="state.paramsQuery" ref="queryRef" :inline="true" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="7" class="colBlock">
|
||||
<el-form-item label="文件名称" prop="fileName">
|
||||
<el-input
|
||||
v-model="state.paramsQuery.fileName"
|
||||
placeholder="请输入文件名称"
|
||||
clearable
|
||||
@keyup.enter.native="getdocumentDataList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" class="m-l10">
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="searchInfo"
|
||||
><el-icon><Search /></el-icon>搜索</el-button
|
||||
>
|
||||
<el-button @click="resetQuery"
|
||||
><el-icon><Refresh /></el-icon>重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table v-loading="state.loading" :data="state.infoList" height="67vh" border>
|
||||
<el-table-column label="序号" align="center" type="index" min-width="50px" />
|
||||
<el-table-column label="文件名称" align="center" prop="fileName"></el-table-column>
|
||||
<el-table-column label="文件类型" align="center" prop="fileSuffix" width="100px" />
|
||||
<el-table-column label="流程状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="wf_business_status" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传时间" align="center" prop="createTime"> </el-table-column>
|
||||
<el-table-column label="操作" align="center" width="300">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link @click="handleApproval(scope.row)"
|
||||
><el-icon><Plus /></el-icon>审批</el-button
|
||||
>
|
||||
<el-button type="primary" link @click="handleViewApproval(scope.row)"
|
||||
><el-icon><View /></el-icon>流程查看</el-button
|
||||
>
|
||||
<el-button type="primary" link @click="handleView(scope.row)" v-if="acceptType.includes(scope.row.fileSuffix)"
|
||||
><el-icon><View /></el-icon>查看</el-button
|
||||
>
|
||||
<el-button type="primary" v-if="state.wordType.includes(scope.row.fileSuffix)" link @click="updataView(scope.row)"
|
||||
><el-icon><EditPen /></el-icon>修改文件</el-button
|
||||
>
|
||||
<el-button type="primary" link @click="onExportView(scope.row)"
|
||||
><el-icon><Download /></el-icon>下载</el-button
|
||||
>
|
||||
<el-button type="success" link @click="updateName(scope.row)"
|
||||
><el-icon><EditPen /></el-icon>修改名称</el-button
|
||||
>
|
||||
<el-button type="danger" link @click="handleDelete(scope.row)"
|
||||
><el-icon><DeleteFilled /></el-icon>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
:total="state.total"
|
||||
v-model:page="state.paramsQuery.pageNum"
|
||||
v-model:limit="state.paramsQuery.pageSize"
|
||||
@pagination="getdocumentDataList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<documentsDeailsVue ref="documentDetailRef" v-if="state.showDocumentDetail" @onClose="onClose"></documentsDeailsVue>
|
||||
<documentsEdit ref="documentDataEditRef" v-if="state.showdocumentDataEdit" @onClose="onCloseEdit"></documentsEdit>
|
||||
<bookFile ref="bookFileRef" @onExportView="onExportView" @onBook="handleView" @onExport="onExport"></bookFile>
|
||||
<el-dialog draggable title="上传文件" v-model="uploadFileder" width="30%">
|
||||
<file-upload v-model="state.paramsQuery.file"></file-upload>
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="uploadFileder = false">取消</el-button>
|
||||
<el-button type="primary" @click="subMitUpload">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<documentsDeailsVue ref="documentDetailRef" v-if="state.showDocumentDetail" @onClose="onClose"></documentsDeailsVue>
|
||||
<documentsEdit ref="documentDataEditRef" v-if="state.showdocumentDataEdit" @onClose="onCloseEdit"></documentsEdit>
|
||||
<bookFile ref="bookFileRef" @onExportView="onExportView" @onBook="handleView" @onExport="onExport"></bookFile>
|
||||
<el-dialog draggable title="上传文件" v-model="uploadFileder" width="30%">
|
||||
<file-upload v-model="state.paramsQuery.file"></file-upload>
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="uploadFileder = false">取消</el-button>
|
||||
<el-button type="primary" @click="subMitUpload">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<el-image-viewer
|
||||
ref="imageRef"
|
||||
style="width: 100%; height: 100%"
|
||||
:url-list="[imgUrl]"
|
||||
v-if="imgUrl"
|
||||
show-progress
|
||||
fit="cover"
|
||||
@close="imgUrl = ''"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回收站" name="second">
|
||||
<RecyclingStation ref="recylingRef"></RecyclingStation>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-image-viewer
|
||||
ref="imageRef"
|
||||
style="width: 100%; height: 100%"
|
||||
:url-list="[imgUrl]"
|
||||
v-if="imgUrl"
|
||||
show-progress
|
||||
fit="cover"
|
||||
@close="imgUrl = ''"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回收站" name="second">
|
||||
<RecyclingStation ref="recylingRef"></RecyclingStation>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="KnowledgeDocument" lang="ts">
|
||||
@ -153,7 +165,6 @@ import {
|
||||
import documentsEdit from './component/documentsEdit.vue';
|
||||
import documentsDeailsVue from './component/documentsDeails.vue';
|
||||
import RecyclingStation from './component/recyclingStation.vue';
|
||||
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import bookFile from './component/bookFile.vue';
|
||||
const activeName = ref('first');
|
||||
@ -162,6 +173,7 @@ const { proxy } = getCurrentInstance() as any;
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
|
||||
const uploadUrl = computed(() => {
|
||||
return `/design/technicalStandard/file`;
|
||||
});
|
||||
@ -171,7 +183,6 @@ const uploadParams = computed(() => {
|
||||
projectId: state.projectId
|
||||
};
|
||||
});
|
||||
|
||||
const imgUrl = ref<string>('');
|
||||
const filterText = ref('');
|
||||
const treeRef = ref();
|
||||
@ -289,7 +300,7 @@ const setInfo = (arr) => {
|
||||
const handleNodeClick = (row) => {
|
||||
state.parentRow = row;
|
||||
state.parentPid = row.parentId;
|
||||
console.log('🚀 ~ handleNodeClick ~ state.parentPid:', state.parentPid);
|
||||
console.log(row);
|
||||
state.parentName = row.label;
|
||||
state.paramsQuery.folderId = row.id;
|
||||
getdocumentDataList();
|
||||
@ -443,6 +454,44 @@ const onBook = () => {
|
||||
onMounted(() => {
|
||||
gettreeStructureData();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
state.projectId = nid;
|
||||
state.paramsQuery.projectId = nid;
|
||||
if (activeName.value === 'first') {
|
||||
gettreeStructureData();
|
||||
} else {
|
||||
// 回收站
|
||||
recylingRef.value.getDocumentDataList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
const handleApproval = (row) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/design-management/technicalStandard/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleViewApproval = (row) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/design-management/technicalStandard/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'view'
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -473,7 +522,7 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
}
|
||||
.tree_left1 {
|
||||
width: 30%;
|
||||
width: 20%;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dddddd;
|
||||
border-radius: 6px;
|
||||
@ -536,7 +585,7 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
.list_right {
|
||||
width: 69.5%;
|
||||
width: 79.5%;
|
||||
background: white;
|
||||
border: 1px solid #ededed;
|
||||
padding: 10px;
|
||||
|
356
src/views/design/technicalStandard/indexEdit.vue
Normal file
356
src/views/design/technicalStandard/indexEdit.vue
Normal file
@ -0,0 +1,356 @@
|
||||
<template>
|
||||
<div class="p-4 bg-gray-50">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<!-- 顶部按钮区域 -->
|
||||
<el-card class="mb-4 rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md">
|
||||
<approvalButton
|
||||
@submitForm="submitForm"
|
||||
@approvalVerifyOpen="approvalVerifyOpen"
|
||||
@handleApprovalRecord="handleApprovalRecord"
|
||||
:buttonLoading="buttonLoading"
|
||||
:id="form.id"
|
||||
:status="form.status"
|
||||
:pageType="routeParams.type"
|
||||
/>
|
||||
</el-card>
|
||||
<!-- 表单区域 -->
|
||||
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
|
||||
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
|
||||
<h3 class="text-lg font-semibold text-gray-800">设计原则</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<el-form
|
||||
ref="leaveFormRef"
|
||||
v-loading="loading"
|
||||
:disabled="routeParams.type === 'view'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
class="space-y-4"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="文件名称" prop="formNo">
|
||||
<el-input disabled v-model="form.fileName" placeholder="请输入文件名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="文件" prop="formNo">
|
||||
<div style="display: flex;">
|
||||
<span style="color: rgb(50, 142, 248);" >{{ form.originalName }}</span>
|
||||
<!-- <el-button type="primary" link @click="handleView(scope.row)"
|
||||
><el-icon><View /></el-icon>查看</el-button
|
||||
> -->
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- 提交组件 -->
|
||||
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
||||
<approvalRecord ref="approvalRecordRef"></approvalRecord>
|
||||
<!-- 流程选择对话框 -->
|
||||
<el-dialog
|
||||
draggable
|
||||
v-model="dialogVisible.visible"
|
||||
:title="dialogVisible.title"
|
||||
:before-close="handleClose"
|
||||
width="500"
|
||||
class="rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="p-4">
|
||||
<p class="text-gray-600 mb-4">请选择要启动的流程:</p>
|
||||
<el-select v-model="flowCode" placeholder="请选择流程" style="width: 100%">
|
||||
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer p-4 border-t border-gray-100 flex justify-end space-x-3">
|
||||
<el-button @click="handleClose" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button type="primary" @click="submitFlow()" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Leave" lang="ts">
|
||||
import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
|
||||
import { startWorkFlow } from '@/api/workflow/task';
|
||||
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
||||
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
||||
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
||||
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
const { design_change_reason_type } = toRefs<any>(proxy?.useDict('design_change_reason_type'));
|
||||
import { getKnowledgeDocument } from '@/api/design/technicalStandard';
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
//路由参数
|
||||
const routeParams = ref<Record<string, any>>({});
|
||||
const flowCode = ref<string>('');
|
||||
const status = ref<string>('');
|
||||
const dialogVisible = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '流程定义'
|
||||
});
|
||||
//提交组件
|
||||
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
||||
//审批记录组件
|
||||
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
//按钮组件
|
||||
const flowCodeOptions = [
|
||||
{
|
||||
value: currentProject.value?.id + '_principletechnical',
|
||||
label: '设计原则审批'
|
||||
},
|
||||
{
|
||||
value: currentProject.value?.id + '_requirementstechnica',
|
||||
label: '业主需求清单审批'
|
||||
}
|
||||
];
|
||||
|
||||
const leaveFormRef = ref<ElFormInstance>();
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
isEdit: false
|
||||
});
|
||||
const submitFormData = ref<StartProcessBo>({
|
||||
businessId: '',
|
||||
flowCode: '',
|
||||
variables: {}
|
||||
});
|
||||
const taskVariables = ref<Record<string, any>>({});
|
||||
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
fileName:undefined,
|
||||
fileUrl:undefined,
|
||||
status:undefined,
|
||||
originalName:undefined
|
||||
};
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
rules: {}
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.visible = false;
|
||||
flowCode.value = '';
|
||||
buttonLoading.value = false;
|
||||
};
|
||||
const { form, rules } = toRefs(data);
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
leaveFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 获取详情 */
|
||||
const getInfo = () => {
|
||||
loading.value = true;
|
||||
buttonLoading.value = false;
|
||||
nextTick(async () => {
|
||||
const res = await getKnowledgeDocument(routeParams.value.id);
|
||||
Object.assign(form.value, res.data);
|
||||
loading.value = false;
|
||||
buttonLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = (status1: string) => {
|
||||
status.value = status1;
|
||||
submit(status.value,form.value);
|
||||
};
|
||||
|
||||
const submitFlow = async () => {
|
||||
handleStartWorkFlow(form.value);
|
||||
dialogVisible.visible = false;
|
||||
};
|
||||
//提交申请
|
||||
const handleStartWorkFlow = async (data: LeaveForm) => {
|
||||
try {
|
||||
submitFormData.value.flowCode = flowCode.value;
|
||||
submitFormData.value.businessId = data.id;
|
||||
//流程变量
|
||||
taskVariables.value = {
|
||||
// leave4/5 使用的流程变量
|
||||
userList: ['1', '3', '4']
|
||||
};
|
||||
submitFormData.value.variables = taskVariables.value;
|
||||
const resp = await startWorkFlow(submitFormData.value);
|
||||
if (submitVerifyRef.value) {
|
||||
buttonLoading.value = false;
|
||||
submitVerifyRef.value.openDialog(resp.data.taskId);
|
||||
}
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
};
|
||||
//审批记录
|
||||
const handleApprovalRecord = () => {
|
||||
approvalRecordRef.value.init(form.value.id);
|
||||
};
|
||||
//提交回调
|
||||
const submitCallback = async () => {
|
||||
await proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
};
|
||||
//审批
|
||||
const approvalVerifyOpen = async () => {
|
||||
submitVerifyRef.value.openDialog(routeParams.value.taskId);
|
||||
};
|
||||
// 图纸上传成功之后 开始提交
|
||||
const submit = async (status, data) => {
|
||||
form.value = data;
|
||||
if (status === 'draft') {
|
||||
buttonLoading.value = false;
|
||||
proxy?.$modal.msgSuccess('暂存成功');
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
} else {
|
||||
if ((form.value.status === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
|
||||
flowCode.value = flowCodeOptions[0].value;
|
||||
dialogVisible.visible = true;
|
||||
return;
|
||||
}
|
||||
//说明启动过先随意穿个参数
|
||||
if (flowCode.value === '' || flowCode.value === null) {
|
||||
flowCode.value = 'xx';
|
||||
}
|
||||
await handleStartWorkFlow(data);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(async () => {
|
||||
routeParams.value = proxy.$route.query;
|
||||
reset();
|
||||
loading.value = false;
|
||||
if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
|
||||
getInfo();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
/* 全局样式 */
|
||||
:root {
|
||||
--primary: #409eff;
|
||||
--primary-light: #66b1ff;
|
||||
--primary-dark: #3a8ee6;
|
||||
--success: #67c23a;
|
||||
--warning: #e6a23c;
|
||||
--danger: #f56c6c;
|
||||
--info: #909399;
|
||||
}
|
||||
|
||||
/* 表单样式优化 */
|
||||
.el-form-item {
|
||||
.el-form-item__label {
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-input__inner,
|
||||
.el-select .el-input__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮样式优化 */
|
||||
.el-button {
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.is-primary {
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-light);
|
||||
border-color: var(--primary-light);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-text {
|
||||
color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-light);
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片样式优化 */
|
||||
.el-card {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
/* transform: translateY(-2px); */
|
||||
}
|
||||
}
|
||||
|
||||
/* 对话框样式优化 */
|
||||
.el-dialog {
|
||||
.el-dialog__header {
|
||||
background-color: #f5f7fa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -430,6 +430,8 @@ export default {
|
||||
return new Promise((resovle, reject) => {
|
||||
if (this.info) {
|
||||
const { cameraIndex, gateway, sn, videoIndex } = this.info;
|
||||
console.log(this.info);
|
||||
|
||||
liveStart({
|
||||
cameraIndex,
|
||||
definition: 0,
|
||||
|
266
src/views/drone/droneConfig/index.vue
Normal file
266
src/views/drone/droneConfig/index.vue
Normal file
@ -0,0 +1,266 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<!-- <el-form-item label="配置名称" prop="configName">
|
||||
<el-input v-model="queryParams.configName" placeholder="请输入配置名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="配置地址" prop="configUrl">
|
||||
<el-input v-model="queryParams.configUrl" placeholder="请输入配置地址" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['drone:droneConfig:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['drone:droneConfig:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['drone:droneConfig:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['drone:droneConfig:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="droneConfigList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" align="center" type="index" width="50" />
|
||||
<!-- <el-table-column label="配置名称" align="center" prop="configName" /> -->
|
||||
<el-table-column label="rtmp端口" align="center" prop="rtmpPort" />
|
||||
<el-table-column label="rtcp端口" align="center" prop="rtcPort" />
|
||||
<el-table-column label="rtmp服务地址" align="center" prop="dockSocketUrl" />
|
||||
<el-table-column label="ai识别服务地址" align="center" prop="aiUrl" />
|
||||
<el-table-column label="srs服务地址" align="center" prop="srsUrl" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['drone:droneConfig:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['drone:droneConfig:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改无人机配置对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="droneConfigFormRef" :model="form" :rules="rules" label-width="150px">
|
||||
<el-form-item label="无人机服务地址" prop="dockSocketUrl">
|
||||
<el-input v-model="form.dockSocketUrl" placeholder="请输入无人机服务地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="ai识别服务地址" prop="aiUrl">
|
||||
<el-input v-model="form.aiUrl" placeholder="请输入ai识别服务地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="srs服务器地址" prop="srsUrl">
|
||||
<el-input v-model="form.srsUrl" placeholder="请输入srs服务器地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="srs(rtmp服务端口)" prop="rtmpPort">
|
||||
<el-input v-model="form.rtmpPort" placeholder="请输入srs(rtmp服务端口)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="srs(webrtc服务端口)" prop="rtcPort">
|
||||
<el-input v-model="form.rtcPort" placeholder="请输入srs(webrtc服务端口)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="DroneConfig" lang="ts">
|
||||
import { listDroneConfig, getDroneConfig, delDroneConfig, addDroneConfig, updateDroneConfig } from '@/api/drone/droneConfig';
|
||||
import { DroneConfigVO, DroneConfigQuery, DroneConfigForm } from '@/api/drone/droneConfig/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const droneConfigList = ref<DroneConfigVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const droneConfigFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: DroneConfigForm = {
|
||||
id: undefined,
|
||||
projectId: currentProject.value.id,
|
||||
configName: undefined,
|
||||
configUrl: undefined,
|
||||
dockSocketUrl: undefined,
|
||||
aiUrl: undefined,
|
||||
srsUrl: undefined,
|
||||
rtmpPort: undefined,
|
||||
rtcPort: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive<PageData<DroneConfigForm, DroneConfigQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value.id,
|
||||
configName: undefined,
|
||||
configUrl: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }],
|
||||
projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询无人机配置列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listDroneConfig(queryParams.value);
|
||||
droneConfigList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
droneConfigFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: DroneConfigVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加无人机配置';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: DroneConfigVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getDroneConfig(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改无人机配置';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
droneConfigFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateDroneConfig(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await addDroneConfig(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: DroneConfigVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除无人机配置编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||||
await delDroneConfig(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'drone/droneConfig/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`droneConfig_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
@ -227,8 +227,10 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<span style="color:#ff0000ab;margin-bottom: 10px;display: block;">注意:请上传doc/xls/ppt/txt/pdf/png/jpg/jpeg/zip格式文件</span>
|
||||
</el-col><el-col :span="24">
|
||||
<span style="color: #ff0000ab; margin-bottom: 10px; display: block"
|
||||
>注意:请上传doc/xls/ppt/txt/pdf/png/jpg/jpeg/zip格式文件</span
|
||||
> </el-col
|
||||
><el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
@ -446,6 +448,19 @@ const handleView = (row) => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.detail {
|
||||
|
@ -460,6 +460,19 @@ const handleView = (row) => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.detail {
|
||||
|
@ -447,6 +447,19 @@ const handleView = (row) => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.detail {
|
||||
|
@ -451,6 +451,19 @@ const handleView = (row) => {
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.detail {
|
||||
|
@ -288,12 +288,16 @@ const getList = async () => {
|
||||
}
|
||||
}
|
||||
loading.value = true;
|
||||
console.log(queryParams.value);
|
||||
loading.value = true;
|
||||
|
||||
const res = await listProgressCategory(queryParams.value);
|
||||
const data = proxy?.handleTree<ProgressCategoryVO>(res.data, 'id', 'pid');
|
||||
if (data) {
|
||||
progressCategoryList.value = data;
|
||||
try {
|
||||
const res = await listProgressCategory(queryParams.value);
|
||||
const data = proxy?.handleTree<ProgressCategoryVO>(res.data, 'id', 'pid');
|
||||
if (data) {
|
||||
progressCategoryList.value = data;
|
||||
}
|
||||
} finally {
|
||||
// 不管成功或失败,最后都设置为 false
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
@ -454,12 +454,6 @@ const initOLMap = () => {
|
||||
})
|
||||
});
|
||||
map.on('click', (e: any) => {
|
||||
var coordinate = e.coordinate;
|
||||
|
||||
// 将投影坐标转换为经纬度坐标
|
||||
var lonLatCoordinate = toLonLat(coordinate);
|
||||
// 输出转换后的经纬度坐标
|
||||
console.log('经纬度坐标:', lonLatCoordinate);
|
||||
const zoom = map.getView().getZoom();
|
||||
const scale = Math.max(zoom / 10, 1); // 缩放比例,根据需要调整公式
|
||||
map.forEachFeatureAtPixel(e.pixel, (feature: Feature) => {
|
||||
@ -539,38 +533,6 @@ const initOLMap = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 你已有的 imageExtent 是 [minX, minY, maxX, maxY]
|
||||
const createExtentBorderLayer = (extent: number[]) => {
|
||||
// 构造矩形坐标,闭合成环,顺序可以是顺时针或逆时针
|
||||
const coords = [
|
||||
[
|
||||
[extent[0], extent[1]],
|
||||
[extent[0], extent[3]],
|
||||
[extent[2], extent[3]],
|
||||
[extent[2], extent[1]],
|
||||
[extent[0], extent[1]]
|
||||
]
|
||||
];
|
||||
|
||||
const polygonFeature = new Feature(new Polygon(coords));
|
||||
|
||||
polygonFeature.setStyle(
|
||||
new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'red', // 你想要的边框颜色
|
||||
width: 3 // 线宽
|
||||
}),
|
||||
fill: null // 不填充,纯边框
|
||||
})
|
||||
);
|
||||
|
||||
return new VectorLayer({
|
||||
source: new VectorSource({
|
||||
features: [polygonFeature]
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
const highlightStyle = (name, scale) => {
|
||||
return new Style({
|
||||
stroke: new Stroke({
|
||||
@ -721,7 +683,7 @@ const addPointToMap = (features: Array<any>) => {
|
||||
};
|
||||
|
||||
//选中几何图形
|
||||
const toggleFeatureHighlight = (feature: Feature, addIfNotExist = true) => {
|
||||
const toggleFeatureHighlight = (feature: Feature) => {
|
||||
const zoom = map.getView().getZoom();
|
||||
const scale = Math.max(zoom / 10, 1);
|
||||
if (feature.get('status') === '2') return;
|
||||
@ -730,41 +692,35 @@ const toggleFeatureHighlight = (feature: Feature, addIfNotExist = true) => {
|
||||
const isHighlighted = feature.get('highlighted') === true;
|
||||
|
||||
if (isHighlighted) {
|
||||
// 如果是反选或 toggle 模式,允许取消
|
||||
feature.setStyle(defaultStyle(feature.get('name'), scale));
|
||||
feature.set('highlighted', false);
|
||||
const id = feature.get('id');
|
||||
const idx = submitForm.value.finishedDetailIdList.indexOf(id);
|
||||
if (idx > -1) submitForm.value.finishedDetailIdList.splice(idx, 1);
|
||||
} else if (addIfNotExist) {
|
||||
} else {
|
||||
feature.setStyle(highlightStyle(feature.get('name'), scale));
|
||||
feature.set('highlighted', true);
|
||||
const id = feature.get('id');
|
||||
if (!submitForm.value.finishedDetailIdList.includes(id)) submitForm.value.finishedDetailIdList.push(id);
|
||||
if (!submitForm.value.finishedDetailIdList.includes(id)) {
|
||||
submitForm.value.finishedDetailIdList.push(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// 地图初始化
|
||||
initOLMap();
|
||||
// geoTiffLoading.value = false;
|
||||
map.addLayer(sharedLayer);
|
||||
selector.value = new LassoSelector(map, sharedSource, (features, isInvert = false) => {
|
||||
features.forEach((feature) => {
|
||||
if (isInvert) {
|
||||
// Shift + 左键 -> 只执行取消选中
|
||||
if (feature.get('highlighted') === true) {
|
||||
toggleFeatureHighlight(feature, false); // 取消选中,addIfNotExist = false
|
||||
}
|
||||
} else {
|
||||
// 普通左键 -> 只执行选中
|
||||
if (feature.get('highlighted') !== true) {
|
||||
toggleFeatureHighlight(feature, true); // 选中,addIfNotExist = true
|
||||
}
|
||||
}
|
||||
console.log(feature.get('status'));
|
||||
|
||||
toggleFeatureHighlight(feature);
|
||||
});
|
||||
});
|
||||
|
||||
enableMiddleMousePan(map);
|
||||
// enableMiddleMousePan(map);
|
||||
getList();
|
||||
creatPoint(fromLonLat([107.13149145799198, 23.804125705140834]), 'Point', '1', '测试点1', '1');
|
||||
});
|
||||
|
@ -1,156 +0,0 @@
|
||||
<template>
|
||||
<div ref="mapContainer" class="map-container"></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import Map from 'ol/Map';
|
||||
import View from 'ol/View';
|
||||
import TileLayer from 'ol/layer/Tile';
|
||||
import XYZ from 'ol/source/XYZ';
|
||||
import TileGrid from 'ol/tilegrid/TileGrid';
|
||||
import { fromLonLat, transform, transformExtent } from 'ol/proj';
|
||||
import { fromUrl } from 'geotiff';
|
||||
import gcoord from 'gcoord';
|
||||
import ImageLayer from 'ol/layer/Image';
|
||||
import Static from 'ol/source/ImageStatic';
|
||||
import { Feature } from 'ol';
|
||||
import { Polygon } from 'ol/geom';
|
||||
import { Stroke, Style } from 'ol/style';
|
||||
import VectorLayer from 'ol/layer/Vector';
|
||||
import VectorSource from 'ol/source/Vector';
|
||||
const mapContainer = ref<HTMLDivElement | null>(null);
|
||||
|
||||
// 您提供的经纬度范围(EPSG:4326)
|
||||
const extent4326 = [107.13149481208748, 23.80411597354268, 107.13487254421389, 23.80801427852998];
|
||||
|
||||
// 计算中心点(经纬度)
|
||||
const centerLon = (extent4326[0] + extent4326[2]) / 2;
|
||||
const centerLat = (extent4326[1] + extent4326[3]) / 2;
|
||||
|
||||
// 转换 extent 到 EPSG:3857
|
||||
const extent3857 = transformExtent(extent4326, 'EPSG:4326', 'EPSG:3857');
|
||||
|
||||
// 生成 resolutions(256像素瓦片)
|
||||
const resolutions = Array.from({ length: 20 }, (_, z) => 156543.03392804097 / Math.pow(2, z));
|
||||
|
||||
onMounted(async () => {
|
||||
// const tiff = await fromUrl('/image/clean_rgba_cleaned.tif');
|
||||
// const image = await tiff.getImage();
|
||||
// const width = image.getWidth();
|
||||
// const height = image.getHeight();
|
||||
// const bbox = image.getBoundingBox(); // [minX, minY, maxX, maxY]
|
||||
// console.log('bbox', bbox);
|
||||
// const rasters = await image.readRasters({ interleave: true });
|
||||
// // 创建 Canvas
|
||||
// const canvas = document.createElement('canvas');
|
||||
// canvas.width = width;
|
||||
// canvas.height = height;
|
||||
// const ctx = canvas.getContext('2d')!;
|
||||
// const imageData: any = ctx.createImageData(width, height);
|
||||
// // 设置 RGBA 数据
|
||||
// imageData.data.set(rasters); // ✅ 完整设置,不用手动循环
|
||||
// ctx.putImageData(imageData, 0, 0);
|
||||
// // 将 canvas 转成 Data URL 用作图层 source
|
||||
// const imageUrl = canvas.toDataURL();
|
||||
// // 转换为 WGS84 经纬度
|
||||
// const minLonLat = transform([bbox[0], bbox[1]], 'EPSG:32648', 'EPSG:4326');
|
||||
// const maxLonLat = transform([bbox[2], bbox[3]], 'EPSG:32648', 'EPSG:4326');
|
||||
// console.log('minLonLat', minLonLat);
|
||||
// console.log('maxLonLat', maxLonLat);
|
||||
// // 转为 GCJ02(高德地图坐标系)
|
||||
// const gcjMin = gcoord.transform(minLonLat as [number, number, number], gcoord.WGS84, gcoord.GCJ02);
|
||||
// const gcjMax = gcoord.transform(maxLonLat as [number, number, number], gcoord.WGS84, gcoord.GCJ02);
|
||||
// // 再转 EPSG:3857 供 OpenLayers 使用
|
||||
// const minXY = fromLonLat(gcjMin);
|
||||
// const maxXY = fromLonLat(gcjMax);
|
||||
|
||||
let imageExtent = [11926280.148303444, 2729254.667731837, 11926657.474014785, 2729714.281185133];
|
||||
console.log('imageExtent', imageExtent);
|
||||
console.log('extent3857', extent3857);
|
||||
if (!mapContainer.value) return;
|
||||
const tileGrid = new TileGrid({
|
||||
extent: imageExtent,
|
||||
origin: [-20037508.342789244, 20037508.342789244], // Web Mercator 左上角
|
||||
resolutions,
|
||||
tileSize: 256
|
||||
});
|
||||
// 高德卫星图图层 (EPSG:3857)
|
||||
const gaodeLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
maxZoom: 19,
|
||||
wrapX: true
|
||||
}),
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
// 自定义瓦片图层(EPSG:3857)
|
||||
const customLayer = new TileLayer({
|
||||
source: new XYZ({
|
||||
url: 'http://192.168.110.2:8000/api/projects/3/tasks/c2e3227f-343f-48b1-88c0-1432d6eab33f/orthophoto/tiles/{z}/{x}/{y}'
|
||||
// extent: imageExtent
|
||||
})
|
||||
});
|
||||
const layer = customLayer; // eg: TileLayer<XYZ>
|
||||
const source = layer.getSource();
|
||||
const projection = source?.getProjection();
|
||||
|
||||
console.log('图层坐标系:', projection?.getCode());
|
||||
const borderLayer = createExtentBorderLayer(imageExtent);
|
||||
const testLayer = createExtentBorderLayer([
|
||||
...fromLonLat([107.13149481208748, 23.80411597354268]),
|
||||
...fromLonLat([107.13487254421389, 23.80801427852998])
|
||||
]);
|
||||
// 创建地图
|
||||
const map = new Map({
|
||||
target: mapContainer.value,
|
||||
layers: [gaodeLayer, customLayer, borderLayer, testLayer],
|
||||
view: new View({
|
||||
projection: 'EPSG:3857',
|
||||
center: fromLonLat([centerLon, centerLat]),
|
||||
zoom: 16,
|
||||
minZoom: 10
|
||||
// extent: extent3857 // 限制视图范围
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
const createExtentBorderLayer = (extent: number[]) => {
|
||||
// 构造矩形坐标,闭合成环,顺序可以是顺时针或逆时针
|
||||
const coords = [
|
||||
[
|
||||
[extent[0], extent[1]],
|
||||
[extent[0], extent[3]],
|
||||
[extent[2], extent[3]],
|
||||
[extent[2], extent[1]],
|
||||
[extent[0], extent[1]]
|
||||
]
|
||||
];
|
||||
|
||||
const polygonFeature = new Feature(new Polygon(coords));
|
||||
|
||||
polygonFeature.setStyle(
|
||||
new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'red', // 你想要的边框颜色
|
||||
width: 3 // 线宽
|
||||
}),
|
||||
fill: null // 不填充,纯边框
|
||||
})
|
||||
);
|
||||
|
||||
return new VectorLayer({
|
||||
source: new VectorSource({
|
||||
features: [polygonFeature]
|
||||
})
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.map-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
@ -7,7 +7,7 @@
|
||||
<el-form-item label="人员姓名" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入人员姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分包公司" prop="contractorId">
|
||||
<el-form-item label="分包公司" prop="contractorId" v-hasPermi="['contractor:contractor:list']">
|
||||
<el-select v-model="queryParams.contractorId" clearable placeholder="全部">
|
||||
<el-option v-for="item in contractorOpt" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
@ -40,13 +40,9 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['project:constructionUser:add']">新增 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:constructionUser:edit']">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['contractor:constructionUser:add']">新增 </el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
@ -54,13 +50,15 @@
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['project:constructionUser:remove']"
|
||||
v-hasPermi="['contractor:constructionUser:remove']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:constructionUser:export']">导出 </el-button>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['contractor:constructionUser:export']"
|
||||
>导出
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Edit" :disabled="multiple" @click="statusDialog = true">用户状态编辑 </el-button>
|
||||
@ -82,18 +80,20 @@
|
||||
<el-button type="success" plain>员工资料 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-show="informationStatus">
|
||||
<el-button type="primary" plain icon="Edit" @click="downloadTemplate">下载资料模板 </el-button>
|
||||
<el-button type="primary" plain icon="Edit" @click="downloadTemplate" v-hasPermi="['contractor:constructionUserFile:download']"
|
||||
>下载资料模板
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-show="informationStatus">
|
||||
<file-upload
|
||||
v-model="filePath"
|
||||
isImportInfo
|
||||
:isShowTip="false"
|
||||
uploadUrl="/project/constructionUserFile/upload/zip"
|
||||
uploadUrl="/contractor/constructionUserFile/upload/zip"
|
||||
:limit="1"
|
||||
:file-size="50"
|
||||
>
|
||||
<el-button type="warning" plain icon="Edit">导入员工资料 </el-button>
|
||||
<el-button type="warning" plain icon="Edit" v-hasPermi="['contractor:constructionUserFile:upload']">导入员工资料 </el-button>
|
||||
</file-upload>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -165,20 +165,28 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" min-width="300">
|
||||
<template #default="scope">
|
||||
<el-space wrap>
|
||||
<el-button link type="primary" icon="View" @click="handleShowDrawer(scope.row)" v-hasPermi="['project:constructionUser:query']">
|
||||
<el-button link type="primary" icon="View" @click="handleShowDrawer(scope.row)" v-hasPermi="['contractor:constructionUser:query']">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:constructionUser:edit']">
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['contractor:constructionUser:edit']">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button link type="warning" icon="Female" @click="handlePlayCard(scope.row)"> 打卡 </el-button>
|
||||
<el-button link type="danger" icon="Avatar" @click="handleJoinBlacklist(scope.row)" v-hasPermi="['project:constructionBlacklist:add']">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Avatar"
|
||||
@click="handleJoinBlacklist(scope.row)"
|
||||
v-hasPermi="['contractor:constructionBlacklist:add']"
|
||||
>
|
||||
黑名单
|
||||
</el-button>
|
||||
<!-- <el-button link type="primary" icon="Switch" @click="handleToggle(scope.row)"> 切换人脸 </el-button> -->
|
||||
<el-button link type="primary" icon="Switch" @click="handleChange(scope.row)"> 人员迁移 </el-button>
|
||||
<el-button link type="primary" icon="Switch" @click="handleChange(scope.row)" v-hasPermi="['contractor:constructionUser:migration']">
|
||||
人员迁移
|
||||
</el-button>
|
||||
<el-button link type="primary" icon="ChatLineSquare" @click="handleExit(scope.row)"> 入退场记录 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:constructionUser:remove']">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['contractor:constructionUser:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
<el-tooltip content="红点:部分上传,绿点:已上传,无点:未上传" placement="right" effect="dark">
|
||||
@ -194,7 +202,7 @@
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改施工人员对话框 -->
|
||||
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="930px" append-to-body>
|
||||
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="930px" append-to-body>
|
||||
<el-form ref="constructionUserFormRef" :model="form" :rules="rules" label-width="130px" :inline="true">
|
||||
<div class="block_box">
|
||||
<div class="msg">用户信息</div>
|
||||
@ -597,7 +605,7 @@ const initFormData: ConstructionUserForm = {
|
||||
salary: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive<PageData<ConstructionUserForm, ConstructionUserQuery>>({
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@ -606,6 +614,7 @@ const data = reactive<PageData<ConstructionUserForm, ConstructionUserQuery>>({
|
||||
nickName: undefined,
|
||||
userName: undefined,
|
||||
projectId: currentProject.value.id,
|
||||
notUserRole: 1,
|
||||
contractorId: undefined,
|
||||
teamId: undefined,
|
||||
status: undefined,
|
||||
@ -780,6 +789,7 @@ const getContractorList = async () => {
|
||||
label: contractor.name
|
||||
}));
|
||||
loading.value = false;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
const handleMonth = async (e: any) => {
|
||||
@ -839,6 +849,7 @@ const reset = () => {
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
if (contractorOpt.value.length == 1) queryParams.value.contractorId = contractorOpt.value[0].value;
|
||||
getList();
|
||||
};
|
||||
|
||||
@ -1063,7 +1074,7 @@ const listeningProject = watch(
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
getContractorList();
|
||||
}
|
||||
);
|
||||
|
||||
@ -1072,7 +1083,6 @@ onUnmounted(() => {
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getContractorList();
|
||||
});
|
||||
</script>
|
||||
|
@ -25,20 +25,20 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['project:contractor:add']"> 新增 </el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['contractor:contractor:add']"> 新增 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:contractor:edit']"
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['contractor:contractor:edit']"
|
||||
>修改
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:contractor:remove']"
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['contractor:contractor:remove']"
|
||||
>删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:contractor:export']">导出 </el-button>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['contractor:contractor:export']">导出 </el-button>
|
||||
</el-col> -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@ -59,12 +59,14 @@
|
||||
<el-table-column label="管理人联系电话" align="center" prop="custodianPhone" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-space wrap>
|
||||
<el-button link type="primary" icon="View" @click="handleContractorFile(scope.row)">文件</el-button>
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:contractor:edit']">修改 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:contractor:remove']">
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['contractor:contractor:edit']"
|
||||
>修改
|
||||
</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['contractor:contractor:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
</el-space>
|
||||
@ -118,7 +120,7 @@ import { addContractor, delContractor, getContractor, listContractor, updateCont
|
||||
import { ContractorForm, ContractorQuery, ContractorVO } from '@/api/project/contractor/types';
|
||||
import ContractorFileDialog from '@/views/project/contractor/component/ContractorFileDialog.vue';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { listData } from '@/api/system/dict/data';
|
||||
import { getDicts, listData } from '@/api/system/dict/data';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@ -179,25 +181,19 @@ const data = reactive<PageData<ContractorForm, ContractorQuery>>({
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
/** 查询字典数据列表 */
|
||||
const getDictList = async () => {
|
||||
const res = await listData({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
dictName: '',
|
||||
dictType: 'contractor_type',
|
||||
dictLabel: ''
|
||||
});
|
||||
dictList.value = res.rows;
|
||||
const res = await getDicts('contractor_type');
|
||||
dictList.value = res.data;
|
||||
};
|
||||
// 分包类型
|
||||
const filterType=(val)=>{
|
||||
let label='';
|
||||
dictList.value.forEach(item=>{
|
||||
if(item.dictValue==val){
|
||||
label=item.dictLabel
|
||||
const filterType = (val) => {
|
||||
let label = '';
|
||||
dictList.value.forEach((item) => {
|
||||
if (item.dictValue == val) {
|
||||
label = item.dictLabel;
|
||||
}
|
||||
})
|
||||
});
|
||||
return label;
|
||||
}
|
||||
};
|
||||
/** 查询分包单位列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
|
@ -89,7 +89,7 @@
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="出入库" placement="top">
|
||||
<el-button link type="primary" icon="view" @click="handleView(scope.row)" ></el-button>
|
||||
<el-button link type="primary" icon="view" @click="handleView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -131,10 +131,10 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog draggable :title="dialogRecord.title" v-model="dialogRecord.visible" width="1500px" append-to-body>
|
||||
<div>
|
||||
<contractorMaterialRecord ref="contractorMaterialRecordRef" ></contractorMaterialRecord>
|
||||
</div>
|
||||
<el-dialog draggable :title="dialogRecord.title" v-model="dialogRecord.visible" width="1500px" append-to-body>
|
||||
<div>
|
||||
<contractorMaterialRecord ref="contractorMaterialRecordRef"></contractorMaterialRecord>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
@ -159,7 +159,7 @@ const currentProject = computed(() => userStore.selectedProject);
|
||||
const contractorMaterialList = ref<ContractorMaterialVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const contractorMaterialRecordRef=ref(null)
|
||||
const contractorMaterialRecordRef = ref(null);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
@ -218,6 +218,7 @@ const getSubList = async () => {
|
||||
projectId: currentProject.value.id
|
||||
});
|
||||
contractorList.value = res.rows;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 查询分包方物料列表 */
|
||||
@ -244,6 +245,7 @@ const reset = () => {
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
if (contractorList.value.length == 1) queryParams.value.contractorId = contractorList.value[0].id;
|
||||
getList();
|
||||
};
|
||||
|
||||
@ -310,7 +312,6 @@ const listeningProject = watch(
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getSubList();
|
||||
getList();
|
||||
}
|
||||
);
|
||||
onUnmounted(() => {
|
||||
@ -318,13 +319,12 @@ onUnmounted(() => {
|
||||
});
|
||||
const handleView = async (row: ContractorToolVO) => {
|
||||
// 打开弹框
|
||||
dialogRecord.visible=true;
|
||||
dialogRecord.title=row.materialName+"-物料出入库";
|
||||
dialogRecord.visible = true;
|
||||
dialogRecord.title = row.materialName + '-物料出入库';
|
||||
await nextTick();
|
||||
contractorMaterialRecordRef.value.getAll(row);
|
||||
};
|
||||
onMounted(() => {
|
||||
getSubList();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
@ -13,8 +13,8 @@
|
||||
<el-input v-model="queryParams.toolName" placeholder="请输入工具名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工具类型" prop="toolType">
|
||||
<el-select v-model="queryParams.toolType" placeholder="请选择工具类型" clearable >
|
||||
<el-option v-for="dict in contractor_tool_type" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
<el-select v-model="queryParams.toolType" placeholder="请选择工具类型" clearable>
|
||||
<el-option v-for="dict in contractor_tool_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工具型号" prop="toolModel">
|
||||
@ -35,13 +35,23 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['project:contractorTool:add']">新增</el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['contractor:contractorTool:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:contractorTool:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['contractor:contractorTool:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:contractorTool:remove']">删除</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['contractor:contractorTool:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@ -52,7 +62,7 @@
|
||||
<el-table-column label="工具名称" align="center" prop="toolName" />
|
||||
<el-table-column label="工具类型" align="center" prop="toolType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="contractor_tool_type" :value="scope.row.toolType"/>
|
||||
<dict-tag :options="contractor_tool_type" :value="scope.row.toolType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工具型号" align="center" prop="toolModel" />
|
||||
@ -66,13 +76,19 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:contractorTool:edit']"></el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['contractor:contractorTool:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:contractorTool:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['contractor:contractorTool:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="出入库" placement="top">
|
||||
<el-button link type="primary" icon="view" @click="handleView(scope.row)" ></el-button>
|
||||
<el-button link type="primary" icon="view" @click="handleView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -83,20 +99,15 @@
|
||||
<el-form ref="contractorToolFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="分包方" prop="contractorId">
|
||||
<el-select v-model="form.contractorId" filterable placeholder="请选择分包方">
|
||||
<el-option v-for="(item, i) of contractorList" :key="i" :label="item.name" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-option v-for="(item, i) of contractorList" :key="i" :label="item.name" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工具名称" prop="toolName">
|
||||
<el-input v-model="form.toolName" placeholder="请输入工具名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工具类型" prop="toolType">
|
||||
<el-select v-model="form.toolType" placeholder="请选择工具类型">
|
||||
<el-option
|
||||
v-for="dict in contractor_tool_type"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
></el-option>
|
||||
<el-option v-for="dict in contractor_tool_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工具型号" prop="toolModel">
|
||||
@ -106,7 +117,7 @@
|
||||
<file-upload v-model="form.file"/>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -116,10 +127,10 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog draggable :title="dialogLevan.title" v-model="dialogLevan.visible" width="1500px" append-to-body>
|
||||
<div>
|
||||
<LevanAutbound ref="LevanAutboundRef" ></LevanAutbound>
|
||||
</div>
|
||||
<el-dialog draggable :title="dialogLevan.title" v-model="dialogLevan.visible" width="1500px" append-to-body>
|
||||
<div>
|
||||
<LevanAutbound ref="LevanAutboundRef"></LevanAutbound>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
@ -127,7 +138,7 @@
|
||||
<script setup name="ContractorTool" lang="ts">
|
||||
import { listContractorTool, getContractorTool, delContractorTool, addContractorTool, updateContractorTool } from '@/api/project/contractorTool';
|
||||
import { ContractorToolVO, ContractorToolQuery, ContractorToolForm } from '@/api/project/contractorTool/types';
|
||||
import { listContractor, } from '@/api/project/contractor';
|
||||
import { listContractor } from '@/api/project/contractor';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import LevanAutbound from '@/views/project/contractorTool/component/LevanAutbound.vue';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@ -139,13 +150,13 @@ const { contractor_tool_type } = toRefs<any>(proxy?.useDict('contractor_tool_typ
|
||||
const contractorToolList = ref<ContractorToolVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const LevanAutboundRef=ref(null)
|
||||
const LevanAutboundRef = ref(null);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const contractorList=ref([]);//分包列表
|
||||
const contractorList = ref([]); //分包列表
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const contractorToolFormRef = ref<ElFormInstance>();
|
||||
|
||||
@ -168,10 +179,10 @@ const initFormData: ContractorToolForm = {
|
||||
toolModel: undefined,
|
||||
toolNumber: undefined,
|
||||
file: undefined,
|
||||
remark: undefined,
|
||||
}
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive<PageData<ContractorToolForm, ContractorToolQuery>>({
|
||||
form: {...initFormData},
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@ -181,30 +192,15 @@ const data = reactive<PageData<ContractorToolForm, ContractorToolQuery>>({
|
||||
toolType: undefined,
|
||||
toolModel: undefined,
|
||||
toolNumber: undefined,
|
||||
params: {
|
||||
}
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
contractorId: [
|
||||
{ required: true, message: "分包方不能为空", trigger: "blur" },
|
||||
|
||||
],
|
||||
toolName: [
|
||||
{ required: true, message: "工具名称不能为空", trigger: "blur" },
|
||||
|
||||
],
|
||||
toolType: [
|
||||
{ required: true, message: "工具类型不能为空", trigger: "blur" },
|
||||
|
||||
],
|
||||
toolModel: [
|
||||
{ required: true, message: "工具数量不能为空", trigger: "blur" },
|
||||
|
||||
],
|
||||
toolNumber: [
|
||||
{ required: true, message: "分包方不能为空", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
contractorId: [{ required: true, message: '分包方不能为空', trigger: 'blur' }],
|
||||
toolName: [{ required: true, message: '工具名称不能为空', trigger: 'blur' }],
|
||||
toolType: [{ required: true, message: '工具类型不能为空', trigger: 'blur' }],
|
||||
toolModel: [{ required: true, message: '工具数量不能为空', trigger: 'blur' }],
|
||||
toolNumber: [{ required: true, message: '分包方不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
const { contractor_tool_record_type } = toRefs<any>(proxy?.useDict('contractor_tool_record_type'));
|
||||
|
||||
@ -214,9 +210,10 @@ const getSubList = async () => {
|
||||
const res = await listContractor({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
projectId: currentProject.value.id,
|
||||
projectId: currentProject.value.id
|
||||
});
|
||||
contractorList.value = res.rows;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 查询分包方工器具列表 */
|
||||
@ -226,55 +223,56 @@ const getList = async () => {
|
||||
contractorToolList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = {...initFormData};
|
||||
form.value = { ...initFormData };
|
||||
contractorToolFormRef.value?.resetFields();
|
||||
}
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
if (contractorList.value.length == 1) queryParams.value.contractorId = contractorList.value[0].id;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: ContractorToolVO[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = "添加工器具";
|
||||
}
|
||||
dialog.title = '添加工器具';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: ContractorToolVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0]
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getContractorTool(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = "修改工器具";
|
||||
}
|
||||
dialog.title = '修改工器具';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
@ -282,25 +280,25 @@ const submitForm = () => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.id) {
|
||||
await updateContractorTool(form.value).finally(() => buttonLoading.value = false);
|
||||
await updateContractorTool(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await addContractorTool(form.value).finally(() => buttonLoading.value = false);
|
||||
await addContractorTool(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
proxy?.$modal.msgSuccess("操作成功");
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: ContractorToolVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除分包方工器具编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
|
||||
await proxy?.$modal.confirm('是否确认删除分包方工器具编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||||
await delContractorTool(_ids);
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
@ -308,8 +306,7 @@ const listeningProject = watch(
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getSubList()
|
||||
getList();
|
||||
getSubList();
|
||||
}
|
||||
);
|
||||
onUnmounted(() => {
|
||||
@ -317,13 +314,12 @@ onUnmounted(() => {
|
||||
});
|
||||
const handleView = async (row: ContractorToolVO) => {
|
||||
// 打开弹框
|
||||
dialogLevan.visible=true;
|
||||
dialogLevan.title=row.toolName+"-工器具出入库";
|
||||
dialogLevan.visible = true;
|
||||
dialogLevan.title = row.toolName + '-工器具出入库';
|
||||
await nextTick();
|
||||
LevanAutboundRef.value.getAll(row);
|
||||
};
|
||||
onMounted(() => {
|
||||
getSubList();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
@ -45,16 +45,36 @@
|
||||
<el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="expand" width="50">
|
||||
<template #default="{ row }">
|
||||
<div class="w187.25 ml-12.5">
|
||||
<div class="w212.25 ml-12.5">
|
||||
<el-button class="mb" type="primary" size="small" @click="handleOpenSetChild(row.id)" icon="plus">添加子项目</el-button>
|
||||
|
||||
<el-table :data="row.children" border stripe>
|
||||
<el-table-column label="序号" type="index" width="55" align="center" />
|
||||
<el-table-column label="名称" align="center" prop="projectName" width="296" />
|
||||
<el-table-column label="名称" align="center" prop="projectName" width="296">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
:type="scope.row.designId ? 'primary' : 'default'"
|
||||
:disabled="!scope.row.designId"
|
||||
@click="handleOpenLayer(scope.row)"
|
||||
v-loading.fullscreen.lock="fullscreenLoading"
|
||||
>{{ scope.row.projectName }}</el-link
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="199" />
|
||||
<el-table-column fixed="right" align="center" label="操作" class-name="small-padding fixed-width" width="199">
|
||||
<el-table-column fixed="right" align="center" label="操作" class-name="small-padding fixed-width" width="299">
|
||||
<template #default="scope">
|
||||
<el-space>
|
||||
<file-upload
|
||||
:limit="1"
|
||||
:fileSize="200"
|
||||
:fileType="['dxf']"
|
||||
v-model:model-value="dxfFile"
|
||||
uploadUrl="/project/projectFile/upload/dxf"
|
||||
:data="{ projectId: scope.row.id }"
|
||||
>
|
||||
<el-button link type="primary" icon="upload">上传DXF </el-button>
|
||||
</file-upload>
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
@ -76,7 +96,7 @@
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="项目名称" align="center" prop="projectName">
|
||||
<template #default="scope">
|
||||
<!-- <template #default="scope">
|
||||
<el-link
|
||||
:type="scope.row.designId ? 'primary' : 'default'"
|
||||
:disabled="!scope.row.designId"
|
||||
@ -84,7 +104,7 @@
|
||||
v-loading.fullscreen.lock="fullscreenLoading"
|
||||
>{{ scope.row.projectName }}</el-link
|
||||
>
|
||||
</template>
|
||||
</template> -->
|
||||
</el-table-column>
|
||||
<el-table-column label="项目简称" align="center" prop="shortName" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
@ -126,16 +146,6 @@
|
||||
<template #default="scope">
|
||||
<el-space>
|
||||
<el-button link type="primary" icon="FolderOpened" @click="handleShowUpload(scope.row)">导入安全协议书 </el-button>
|
||||
<file-upload
|
||||
:limit="1"
|
||||
:fileSize="200"
|
||||
:fileType="['dxf']"
|
||||
v-model:model-value="dxfFile"
|
||||
uploadUrl="/project/projectFile/upload/dxf"
|
||||
:data="{ projectId: scope.row.id }"
|
||||
>
|
||||
<el-button link type="primary" icon="upload">上传DXF </el-button>
|
||||
</file-upload>
|
||||
|
||||
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:project:edit']">修改 </el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:project:remove']">删除 </el-button>
|
||||
|
@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="block_box">
|
||||
<span>用户信息</span>
|
||||
<el-form label-width="130px">
|
||||
<el-row :gutter="20" justify="space-around">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="人脸照">
|
||||
<el-image :src="userDetail?.facePicUrl" style="width: 150px; height: 150px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名">
|
||||
{{ userDetail?.userName }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话">
|
||||
{{ userDetail?.phone }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="性别">
|
||||
<dict-tag :options="user_sex_type" :value="userDetail?.sex" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年龄">
|
||||
{{ dayjs().diff(dayjs(userDetail?.sfzBirth), 'year') }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="民族">
|
||||
{{ userDetail?.nation }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="籍贯">
|
||||
{{ userDetail?.nativePlace }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证号码">
|
||||
{{ userDetail?.sfzNumber }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证号码">
|
||||
{{ userDetail?.sfzNumber }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证有效开始期">
|
||||
{{ dayjs(userDetail?.sfzStart).format('YYYY 年 MM 月 DD 日') }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证有效结束期">
|
||||
{{ dayjs(userDetail?.sfzEnd).format('YYYY 年 MM 月 DD 日') }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份证地址">
|
||||
{{ userDetail?.sfzSite }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="block_box">
|
||||
<span>银行卡</span>
|
||||
<el-form label-width="130px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="银行卡号">
|
||||
{{ userDetail?.yhkNumber }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="银行开户行">
|
||||
{{ userDetail?.yhkOpeningBank }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="持卡人">
|
||||
{{ userDetail?.yhkCardholder }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="block_box">
|
||||
<span>单位信息</span>
|
||||
<el-form label-width="130px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="施工单位">
|
||||
{{ userDetail?.contractorVo?.name }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="工种">
|
||||
<dict-tag :options="type_of_work" :value="userDetail?.typeOfWork" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="block_box">
|
||||
<span>其他信息</span>
|
||||
<el-form label-width="130px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班组">
|
||||
{{ userDetail?.teamVo?.teamName }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="打卡状态">
|
||||
<dict-tag :options="user_clock_type" :value="userDetail?.clock" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="入场时间">
|
||||
{{ userDetail?.entryDate ? dayjs(userDetail?.entryDate).format('YYYY 年 MM 月 DD 日 HH:mm:ss') : '' }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="离场时间">
|
||||
{{ userDetail?.leaveDate ? dayjs(userDetail?.leaveDate).format('YYYY 年 MM 月 DD 日 HH:mm:ss') : '' }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getConstructionUser } from '@/api/project/constructionUser';
|
||||
import { ConstructionUserVO } from '@/api/project/constructionUser/types';
|
||||
import { dayjs } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { type_of_work, user_sex_type, user_clock_type } = toRefs<any>(proxy?.useDict('type_of_work', 'user_sex_type', 'user_clock_type'));
|
||||
|
||||
interface Props {
|
||||
userId?: string | number;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const loading = ref<boolean>(false);
|
||||
const userDetail = ref<ConstructionUserVO>();
|
||||
const getUserDetail = async () => {
|
||||
loading.value = true;
|
||||
const res = await getConstructionUser(props.userId);
|
||||
if (res.data && res.code === 200) {
|
||||
userDetail.value = res.data;
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getUserDetail();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.userId,
|
||||
(newId, oldId) => {
|
||||
if (newId !== oldId) {
|
||||
getUserDetail();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.block_box {
|
||||
border: 1px solid #9eccfa;
|
||||
border-radius: 6px;
|
||||
padding: 10px 20px 20px 10px;
|
||||
margin: 15px;
|
||||
> span {
|
||||
color: #409eff;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
1181
src/views/project/projectUser/index.vue
Normal file
1181
src/views/project/projectUser/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@ -33,15 +33,15 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['project:subcontract:add']">新增</el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['contractor:subcontract:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:subcontract:edit']"
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['contractor:subcontract:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:subcontract:remove']"
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['contractor:subcontract:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
@ -69,10 +69,16 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:subcontract:edit']"></el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['contractor:subcontract:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:subcontract:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['contractor:subcontract:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -80,15 +86,15 @@
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改分包合同对话框 -->
|
||||
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="subcontractFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="分包方" prop="contractorId">
|
||||
<el-select v-model="form.contractorId" filterable placeholder="请选择分包方">
|
||||
<el-option v-for="(item, i) of contractorList" :key="i" :label="item.name" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-option v-for="(item, i) of contractorList" :key="i" :label="item.name" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同文件" prop="contractFileId">
|
||||
<file-upload fileSize="" :fileType="['doc', 'xls', 'ppt', 'txt', 'pdf', 'png', 'jpg', 'jpeg']" v-model="form.contractFileId" />
|
||||
<file-upload :fileType="['doc', 'xls', 'ppt', 'txt', 'pdf', 'png', 'jpg', 'jpeg']" v-model="form.contractFileId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同名称" prop="contractName">
|
||||
<el-input v-model="form.contractName" placeholder="请输入合同名称" />
|
||||
@ -105,8 +111,7 @@
|
||||
<el-input v-model="form.contractAmount" type="number" placeholder="请输入合同金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同日期" prop="contractTime">
|
||||
<el-date-picker clearable v-model="form.contractTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择合同日期">
|
||||
</el-date-picker>
|
||||
<el-date-picker clearable v-model="form.contractTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择合同日期"> </el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
@ -125,7 +130,7 @@
|
||||
<script setup name="Subcontract" lang="ts">
|
||||
import { listSubcontract, getSubcontract, delSubcontract, addSubcontract, updateSubcontract } from '@/api/project/subcontract';
|
||||
import { SubcontractVO, SubcontractQuery, SubcontractForm } from '@/api/project/subcontract/types';
|
||||
import { listContractor, } from '@/api/project/contractor';
|
||||
import { listContractor } from '@/api/project/contractor';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@ -141,7 +146,7 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const contractorList=ref([]);//分包列表
|
||||
const contractorList = ref([]); //分包列表
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const subcontractFormRef = ref<ElFormInstance>();
|
||||
|
||||
@ -181,7 +186,7 @@ const data = reactive<PageData<SubcontractForm, SubcontractQuery>>({
|
||||
contractNumber: [{ required: true, message: '合同编号不能为空', trigger: 'change' }],
|
||||
contractName: [{ required: true, message: '合同名称不能为空', trigger: 'change' }],
|
||||
contractAmount: [{ required: true, message: '合同金额不能为空', trigger: 'change' }],
|
||||
contractTime: [{ required: true, message: '合同时间不能为空', trigger: 'change' }],
|
||||
contractTime: [{ required: true, message: '合同时间不能为空', trigger: 'change' }]
|
||||
}
|
||||
});
|
||||
|
||||
@ -200,9 +205,11 @@ const getSubList = async () => {
|
||||
const res = await listContractor({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
projectId: currentProject.value.id,
|
||||
projectId: currentProject.value.id
|
||||
});
|
||||
contractorList.value = res.rows;
|
||||
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
@ -220,6 +227,7 @@ const reset = () => {
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
if (contractorList.value.length == 1) queryParams.value.contractorId = contractorList.value[0].id;
|
||||
getList();
|
||||
};
|
||||
|
||||
@ -285,7 +293,7 @@ const listeningProject = watch(
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
getSubList();
|
||||
}
|
||||
);
|
||||
|
||||
@ -294,6 +302,5 @@ onUnmounted(() => {
|
||||
});
|
||||
onMounted(() => {
|
||||
getSubList();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
@ -23,10 +23,30 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" :offset="0" prop="dept">
|
||||
<el-form-item label="部门">
|
||||
<el-cascader
|
||||
:options="postListAll"
|
||||
v-model="formData.dept"
|
||||
clearable
|
||||
filterable
|
||||
:show-all-levels="false"
|
||||
placeholder="请选择部门"
|
||||
:props="{ expandTrigger: 'hover', checkStrictly: true, value: 'id', emitPath: false, multiple: true }"
|
||||
@change="changeDept"
|
||||
>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="岗位" prop="postIdList">
|
||||
<el-select v-model="formData.postIdList" placeholder="请选择岗位" multiple>
|
||||
<el-option v-for="dict in postListAll" :key="dict.postId" :label="dict.postName" :value="dict.postId" />
|
||||
<el-select v-model="formData.postIdList" placeholder="请选择岗位" multiple :disabled="!formData.dept">
|
||||
<el-option
|
||||
v-for="dict in formData.postWorkList"
|
||||
:key="dict.postId"
|
||||
:label="dict.deptName + '/' + dict.postName"
|
||||
:value="dict.postId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -60,7 +80,7 @@ const userStore = useUserStoreHook();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
|
||||
// Props
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
tourTypeOptions: any[];
|
||||
risxList: any[];
|
||||
postListAll: any[];
|
||||
@ -78,6 +98,7 @@ const loading = ref(false);
|
||||
const formData = reactive({
|
||||
id: undefined,
|
||||
violationLevel: undefined,
|
||||
dept: undefined,
|
||||
color: undefined,
|
||||
violationType: undefined as any,
|
||||
wxOrPc: undefined,
|
||||
@ -88,6 +109,7 @@ const formData = reactive({
|
||||
deletedAt: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
riskType: '',
|
||||
postWorkList: [] as any[],
|
||||
postIdList: [] as any[]
|
||||
});
|
||||
|
||||
@ -127,6 +149,7 @@ const resetForm = () => {
|
||||
violationLevel: undefined,
|
||||
color: undefined,
|
||||
violationType: undefined,
|
||||
dept: undefined,
|
||||
wxOrPc: undefined,
|
||||
createBy: undefined,
|
||||
updateBy: undefined,
|
||||
@ -135,6 +158,7 @@ const resetForm = () => {
|
||||
deletedAt: undefined,
|
||||
projectId: currentProject.value?.id,
|
||||
riskType: '',
|
||||
postWorkList: [] as any[],
|
||||
postIdList: []
|
||||
});
|
||||
};
|
||||
@ -165,6 +189,32 @@ const onSubmit = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const changeDept = (val: any) => {
|
||||
formData.postWorkList = getPostVoListByIds(props.postListAll, val);
|
||||
formData.postIdList = [];
|
||||
console.log(formData.postWorkList, val);
|
||||
};
|
||||
|
||||
function getPostVoListByIds(tree: any[], idList: (string | number)[]): any[] {
|
||||
const idSet = new Set(idList.map(String)); // 用于快速匹配 ID(统一为字符串比较)
|
||||
const result: any[] = [];
|
||||
|
||||
function dfs(nodes: any[]) {
|
||||
for (const node of nodes) {
|
||||
if (idSet.has(String(node.id))) {
|
||||
result.push(...(node.postVoList || []));
|
||||
}
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
dfs(node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dfs(tree);
|
||||
return result;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
closeDialog
|
||||
|
@ -25,10 +25,30 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" :offset="0" prop="dept">
|
||||
<el-form-item label="部门">
|
||||
<el-cascader
|
||||
:options="postListAll"
|
||||
v-model="formData.dept"
|
||||
clearable
|
||||
filterable
|
||||
:show-all-levels="false"
|
||||
placeholder="请选择部门"
|
||||
:props="{ expandTrigger: 'hover', checkStrictly: true, value: 'id', emitPath: false, multiple: true }"
|
||||
@change="changeDept"
|
||||
>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="岗位" prop="postIdList">
|
||||
<el-select v-model="formData.postIdList" placeholder="请选择岗位" multiple>
|
||||
<el-option v-for="dict in postListAll" :key="dict.postId" :label="dict.postName" :value="dict.postId" />
|
||||
<el-select v-model="formData.postIdList" placeholder="请选择岗位" multiple :disabled="!formData.dept">
|
||||
<el-option
|
||||
v-for="dict in formData.postWorkList"
|
||||
:key="dict.postId"
|
||||
:label="dict.deptName + '/' + dict.postName"
|
||||
:value="dict.postId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -57,7 +77,7 @@ import { ElMessage, ElLoading } from 'element-plus';
|
||||
import { getViolationLevel, addViolationLevel, updateViolationLevel } from '@/api/safety/violationLevel';
|
||||
import { ViolationLevelForm, ViolationLevelVO } from '@/api/safety/violationLevel/types';
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
tourTypeOptions: any[];
|
||||
risxList: any[];
|
||||
postListAll: any[];
|
||||
@ -75,11 +95,16 @@ const formData = reactive<
|
||||
risx?: string[] | string;
|
||||
tourType?: string[] | string;
|
||||
postIdList: number[];
|
||||
dept: any;
|
||||
postWorkList: any[];
|
||||
}
|
||||
>({
|
||||
id: undefined,
|
||||
violationLevel: undefined,
|
||||
color: undefined,
|
||||
dept: undefined,
|
||||
postWorkList: [] as any[],
|
||||
|
||||
riskType: undefined,
|
||||
createBy: undefined,
|
||||
updateBy: undefined,
|
||||
@ -115,6 +140,8 @@ const openDialog = (row?: ViolationLevelVO) => {
|
||||
const data = res.data;
|
||||
data.postIdList = (data.postList || []).map((item: any) => item.postId);
|
||||
data.violationType = data.violationType ? (data.violationType as string).split(',') : [];
|
||||
formData.dept = (data.postList || []).map((item: any) => item.deptId);
|
||||
formData.postWorkList = getPostVoListByIds(props.postListAll, formData.dept);
|
||||
Object.assign(formData, data);
|
||||
});
|
||||
});
|
||||
@ -174,11 +201,41 @@ const resetForm = () => {
|
||||
createdAt: undefined,
|
||||
updatedAt: undefined,
|
||||
deletedAt: undefined,
|
||||
dept: undefined,
|
||||
postWorkList: [],
|
||||
postIdList: [],
|
||||
|
||||
risx: '',
|
||||
posts: []
|
||||
});
|
||||
};
|
||||
|
||||
const changeDept = (val: any) => {
|
||||
formData.postWorkList = getPostVoListByIds(props.postListAll, val);
|
||||
formData.postIdList = [];
|
||||
console.log(formData.postWorkList, val);
|
||||
};
|
||||
|
||||
function getPostVoListByIds(tree: any[], idList: (string | number)[]): any[] {
|
||||
const idSet = new Set(idList.map(String)); // 用于快速匹配 ID(统一为字符串比较)
|
||||
const result: any[] = [];
|
||||
|
||||
function dfs(nodes: any[]) {
|
||||
for (const node of nodes) {
|
||||
if (idSet.has(String(node.id))) {
|
||||
result.push(...(node.postVoList || []));
|
||||
}
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
dfs(node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dfs(tree);
|
||||
return result;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
closeDialog
|
||||
|
@ -106,7 +106,7 @@ import { ViolationLevelForm, ViolationLevelVO } from '@/api/safety/violationLeve
|
||||
import apiV1SystemBusViolationLevelAdd from '@/views/safety/violationLevel/component/add.vue';
|
||||
import apiV1SystemBusViolationLevelEdit from '@/views/safety/violationLevel/component/edit.vue';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { listPost, optionselect } from '@/api/system/post';
|
||||
import { listPost, listTreeByProject, optionselect } from '@/api/system/post';
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
@ -145,8 +145,9 @@ const state = reactive<any>({
|
||||
});
|
||||
|
||||
const postList = () => {
|
||||
listPost({ pageNum: 1, pageSize: 100 }).then((res) => {
|
||||
state.postListAll = res.rows ?? [];
|
||||
listTreeByProject(currentProject.value?.id).then((res) => {
|
||||
console.log('🚀 ~ listTreeByProject ~ res:', res);
|
||||
state.postListAll = res.data ?? [];
|
||||
});
|
||||
};
|
||||
|
||||
@ -157,8 +158,6 @@ const initTableData = () => {
|
||||
const resetQuery = (formEl?: FormInstance) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
console.log(useUserStoreHook().selectedProject, currentProject.value?.id);
|
||||
|
||||
busViolationLevelList();
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<!-- <el-card v-loading="loading" body-class="printMe"> -->
|
||||
<div class="w75% m-a">
|
||||
<div id="printMe" class="pos-relative">
|
||||
<div class="resultIcon"><img :src="'../../../../../src/assets/icons/svg/' + inspectionType + '.png'" alt="" /></div>
|
||||
<!-- <h2 style="text-align: center; margin-top: 5px; font-weight: bold">安全生产监督检查通知书</h2> -->
|
||||
<el-row>
|
||||
<el-col :span="12" style="text-align: left">填报人:{{ safetyInspectionDetail?.createByName }}</el-col>
|
||||
<el-col :span="12" style="text-align: right">填报时间:{{ safetyInspectionDetail?.createTime }}</el-col>
|
||||
</el-row>
|
||||
<el-descriptions :column="2" border style="margin-top: 8px" label-width="160px" size="large">
|
||||
<el-descriptions-item label-align="center" label="检查项目" :span="2" class-name="zebra">{{ currentProject?.name }} </el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label-align="center" label="违章类型" label-class-name="white" width="300px">
|
||||
<dict-tag :options="violation_level_type" :value="safetyInspectionDetail?.violationType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="违章等级" label-class-name="white">
|
||||
<div class="flex">
|
||||
{{ safetyInspectionDetail?.levelVo.violationLevel }}:<dict-tag
|
||||
:options="risk_level_type"
|
||||
:value="safetyInspectionDetail?.levelVo.riskType"
|
||||
/>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="检查时间" class-name="zebra"
|
||||
>{{ safetyInspectionDetail?.violationTime }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="检查人" class-name="zebra"
|
||||
>{{ safetyInspectionDetail?.createByName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="整改人" label-class-name="white"
|
||||
>{{ safetyInspectionDetail?.handlerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="要求整改期限" label-class-name="white">
|
||||
{{ safetyInspectionDetail?.disposeDeadline ? dayjs(safetyInspectionDetail?.disposeDeadline).format('YYYY - MM - DD ') : '' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions border direction="vertical" size="large">
|
||||
<el-descriptions-item label-align="center" label="巡检结果" class-name="none"></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="2" border label-width="160px" size="large">
|
||||
<el-descriptions-item label-align="center" label="内容" :span="2" label-class-name="white"
|
||||
>{{ safetyInspectionDetail?.recognizeVo.description }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="检查附件" :span="2" label-class-name="white">
|
||||
<el-space wrap>
|
||||
<!-- <div v-for="item in checkFileList" :key="item.ossId">
|
||||
<span v-if="['.png', '.jpg', '.jpeg'].includes(item.fileSuffix)">
|
||||
<image-preview :src="item.url" width="200px" />
|
||||
</span>
|
||||
<span v-else>
|
||||
<el-link :href="`${item.url}`" type="primary" :underline="false" target="_blank">
|
||||
<span> {{ item.originalName }} </span>
|
||||
</el-link>
|
||||
</span>
|
||||
</div> -->
|
||||
<span>
|
||||
<image-preview :src="safetyInspectionDetail?.recognizeVo.picture" width="200px" />
|
||||
</span>
|
||||
</el-space>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="center" label="检查状态" :span="2" label-class-name="white">
|
||||
<el-steps style="max-width: 200px" :active="Number(safetyInspectionDetail?.status)" finish-status="finish">
|
||||
<el-step v-for="item in safety_inspection_type" :key="item.value" :title="item.label" />
|
||||
</el-steps>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- </el-card> -->
|
||||
<!-- <div class="dialog-footer">
|
||||
<div class="btn-item" @click="handleExport">
|
||||
<img src="../../../../assets/icons/svg/derived.png" />
|
||||
<span>导出</span>
|
||||
</div>
|
||||
<div class="btn-item" v-print="'#printMe'">
|
||||
<img src="../../../../assets/icons/svg/print.png" />
|
||||
<span>打印</span>
|
||||
</div>
|
||||
</div> -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { downLoadOss, listByIds } from '@/api/system/oss';
|
||||
import { OssVO } from '@/api/system/oss/types';
|
||||
import { dayjs } from 'element-plus';
|
||||
import { getViolationRecord } from '@/api/safety/violationRecord';
|
||||
import { ViolationRecordVO } from '@/api/safety/violationRecord/types';
|
||||
|
||||
interface Props {
|
||||
violationRecordId?: string | number;
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { violation_level_type, safety_inspection_type, risk_level_type } = toRefs<any>(
|
||||
proxy?.useDict('violation_level_type', 'review_type', 'reply_type', 'safety_inspection_type', 'risk_level_type')
|
||||
);
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const props = defineProps<Props>();
|
||||
const loading = ref<boolean>(false);
|
||||
const safetyInspectionDetail = ref<any>();
|
||||
const checkFileList = ref<OssVO[]>();
|
||||
const rectificationFileList = ref<OssVO[]>();
|
||||
//检查状态图片
|
||||
const inspectionType = computed(() => {
|
||||
let imgName = 'successLogo';
|
||||
if (safetyInspectionDetail.value?.status == '2') imgName = 'rectification';
|
||||
if (safetyInspectionDetail.value?.reviewType == '1') imgName = 'successful';
|
||||
if (safetyInspectionDetail.value?.reviewType == '2') imgName = 'failure';
|
||||
console.log('🚀 ~ inspectionType ~ imgName:', imgName);
|
||||
|
||||
return imgName;
|
||||
});
|
||||
|
||||
const get = async () => {
|
||||
loading.value = true;
|
||||
const res = await getViolationRecord(props.violationRecordId);
|
||||
if (res.data && res.code === 200) {
|
||||
safetyInspectionDetail.value = res.data;
|
||||
if (res.data.checkFile) {
|
||||
const checkFileRes = await listByIds(res.data.checkFile.split(','));
|
||||
checkFileList.value = checkFileRes.data;
|
||||
}
|
||||
if (res.data.rectificationFile) {
|
||||
const rectificationFileRes = await listByIds(res.data.rectificationFile.split(','));
|
||||
rectificationFileList.value = rectificationFileRes.data;
|
||||
}
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const handleExport = async () => {
|
||||
await downLoadOss({ id: safetyInspectionDetail.value.id }, '/safety/safetyInspection/export/word', '安全生产监督检查通知书.zip');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('🚀 ~ onMounted ~ props.safetyInspectionId:', props.violationRecordId);
|
||||
get();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.violationRecordId,
|
||||
(newId, oldId) => {
|
||||
if (newId !== oldId) {
|
||||
checkFileList.value = undefined;
|
||||
rectificationFileList.value = undefined;
|
||||
get();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#printMe {
|
||||
padding: 15px 20px 20px 20px !important;
|
||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
:deep(.white) {
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
:deep(.none) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.zebra) {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 0mm;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: absolute;
|
||||
top: 14%;
|
||||
right: 6%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 10px #ddd;
|
||||
text-align: center;
|
||||
padding: 20px 10px;
|
||||
|
||||
.btn-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.resultIcon {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
right: 50px;
|
||||
z-index: 10;
|
||||
width: 105px;
|
||||
height: 105px;
|
||||
img {
|
||||
width: 105px;
|
||||
}
|
||||
}
|
||||
</style>
|
307
src/views/safety/violationRecord/index.vue
Normal file
307
src/views/safety/violationRecord/index.vue
Normal file
@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="工单号" prop="id">
|
||||
<el-input v-model="queryParams.id" placeholder="请输入工单号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="违章时间" prop="violationTime">
|
||||
<el-date-picker clearable v-model="queryParams.violationTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择违章时间" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['safety:violationRecord:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="violationRecordList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="工单号" align="center" prop="id" v-if="true" />
|
||||
<el-table-column label="违章等级" align="center" prop="levelVo" width="180">
|
||||
<template #default="scope">
|
||||
<div class="flex justify-center">
|
||||
{{ scope.row.levelVo.violationLevel }}:<dict-tag :options="risk_level_type" :value="scope.row.levelVo.riskType" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="违章类型" align="center" prop="violationType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="violation_level_type" :value="scope.row.violationType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="违章时间" align="center" prop="violationTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.violationTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="处理人" align="center" prop="handlerName" />
|
||||
<el-table-column label="整改措施" align="center" prop="measure" />
|
||||
<el-table-column label="整改时间" align="center" prop="rectificationTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.rectificationTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="复查情况" align="center" prop="review" />
|
||||
<el-table-column label="复查状态" align="center" prop="reviewType">
|
||||
<template #default="scope">
|
||||
{{ scope.row.reviewType ? (scope.row.reviewType == 1 ? '已通过' : '未通过') : '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="复查时间" align="center" prop="reviewTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.reviewTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="处理流程类型(0仅通知 1通知整改复查)" align="center" prop="processType" /> -->
|
||||
<el-table-column label="工单状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="safety_inspection_type" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="详情" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="View"
|
||||
@click="handleShowDialog(scope.row)"
|
||||
v-hasPermi="['safety:violationRecord:view']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="违章处理人" placement="top">
|
||||
<el-button link type="primary" icon="User" @click="handleUpdate(scope.row)" v-hasPermi="['safety:violationRecord:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['safety:violationRecord:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改违规记录对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="violationRecordFormRef" :model="form" :rules="rules" label-width="90px">
|
||||
<el-form-item label="违章处理人" prop="handlerId">
|
||||
<el-input v-model="form.handlerId" placeholder="请输入违章处理人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="处理期限" prop="disposeDeadline">
|
||||
<el-date-picker clearable v-model="form.disposeDeadline" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择处理期限">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog title="违规记录详情" v-model="showDetailDialog" width="60vw">
|
||||
<ViolationRecordDetailDialog :violation-record-id="currentViolationRecordId" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ViolationRecord" lang="ts">
|
||||
import { listViolationRecord, getViolationRecord, delViolationRecord, addViolationRecord, updateViolationRecord } from '@/api/safety/violationRecord';
|
||||
import { ViolationRecordVO, ViolationRecordQuery, ViolationRecordForm } from '@/api/safety/violationRecord/types';
|
||||
import ViolationRecordDetailDialog from './component/violationRecordDetailDialog.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { violation_level_type, risk_level_type, safety_inspection_type } = toRefs<any>(
|
||||
proxy?.useDict('violation_level_type', 'risk_level_type', 'safety_inspection_type')
|
||||
);
|
||||
|
||||
const violationRecordList = ref<ViolationRecordVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const currentViolationRecordId = ref<string | number>(undefined);
|
||||
const showDetailDialog = ref(false);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const violationRecordFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: ViolationRecordForm = {
|
||||
id: undefined,
|
||||
projectId: undefined,
|
||||
levelId: undefined,
|
||||
recognizeId: undefined,
|
||||
violationType: undefined,
|
||||
violationTime: undefined,
|
||||
handlerId: undefined,
|
||||
disposeDeadline: undefined,
|
||||
disposeTime: undefined,
|
||||
measure: undefined,
|
||||
rectificationTime: undefined,
|
||||
review: undefined,
|
||||
reviewType: undefined,
|
||||
reviewTime: undefined,
|
||||
processType: undefined,
|
||||
status: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
const data = reactive<PageData<ViolationRecordForm, ViolationRecordQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
id: undefined,
|
||||
projectId: undefined,
|
||||
violationType: undefined,
|
||||
violationTime: undefined,
|
||||
handlerId: undefined,
|
||||
disposeDeadline: undefined,
|
||||
disposeTime: undefined,
|
||||
measure: undefined,
|
||||
rectificationTime: undefined,
|
||||
review: undefined,
|
||||
reviewType: undefined,
|
||||
reviewTime: undefined,
|
||||
processType: undefined,
|
||||
status: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }],
|
||||
projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }],
|
||||
levelId: [{ required: true, message: '违章等级id不能为空', trigger: 'blur' }],
|
||||
recognizeId: [{ required: true, message: '识别记录id不能为空', trigger: 'blur' }],
|
||||
processType: [{ required: true, message: '处理流程类型(0仅通知 1通知整改复查)不能为空', trigger: 'change' }],
|
||||
status: [{ required: true, message: '工单状态不能为空', trigger: 'change' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询违规记录列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listViolationRecord(queryParams.value);
|
||||
violationRecordList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
violationRecordFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: ViolationRecordVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加违规记录';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: ViolationRecordVO) => {
|
||||
reset();
|
||||
form.value.id = row.id;
|
||||
dialog.visible = true;
|
||||
dialog.title = '选择违章处理人';
|
||||
};
|
||||
|
||||
const handleShowDialog = (row?: ViolationRecordVO) => {
|
||||
currentViolationRecordId.value = row.id;
|
||||
showDetailDialog.value = true;
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
violationRecordFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
await addViolationRecord(form.value).finally(() => (buttonLoading.value = false));
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: ViolationRecordVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除违规记录编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
||||
await delViolationRecord(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'safety/violationRecord/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`violationRecord_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
@ -47,6 +47,11 @@
|
||||
>
|
||||
<el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
|
||||
<el-table-column prop="deptCategory" align="center" label="类别编码" width="200"></el-table-column>
|
||||
<el-table-column prop="deptType" align="center" label="部门类型" width="200">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_dept_type" :value="scope.row.deptType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orderNum" align="center" label="排序" width="200"></el-table-column>
|
||||
<el-table-column prop="status" align="center" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
@ -63,7 +68,7 @@
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:dept:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-tooltip content="新增" placement="top" v-if="scope.row.deptType != '2' && scope.row.deptType != '4'">
|
||||
<el-button v-hasPermi="['system:dept:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
@ -75,7 +80,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-body width="600px">
|
||||
<el-form ref="deptFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form ref="deptFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-row>
|
||||
<el-col v-if="form.parentId !== 0" :span="24">
|
||||
<el-form-item label="上级部门" prop="parentId">
|
||||
@ -128,6 +133,27 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="部门类型">
|
||||
<el-select v-model="form.deptType" placeholder="请选择部门类型" @change="changeProject">
|
||||
<el-option v-for="dict in sys_dept_type" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.deptType == '3'">
|
||||
<el-form-item label="所属项目" prop="projectId">
|
||||
<el-select v-model="form.projectId" placeholder="请选择所属项目">
|
||||
<el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.deptType == '4'">
|
||||
<el-form-item label="分包单位" prop="contractorId">
|
||||
<el-select v-model="form.contractorId" placeholder="请选择分包单位">
|
||||
<el-option v-for="item in contractorList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@ -141,7 +167,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="Dept" lang="ts">
|
||||
import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from '@/api/system/dept';
|
||||
import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild, getDeptList, optionProjectSelect } from '@/api/system/dept';
|
||||
import { DeptForm, DeptQuery, DeptVO } from '@/api/system/dept/types';
|
||||
import { UserVO } from '@/api/system/user/types';
|
||||
import { listUserByDeptId } from '@/api/system/user';
|
||||
@ -153,7 +179,7 @@ interface DeptOptionsType {
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
const { sys_normal_disable, sys_dept_type } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_dept_type'));
|
||||
|
||||
const deptList = ref<DeptVO[]>([]);
|
||||
const loading = ref(true);
|
||||
@ -166,6 +192,8 @@ const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
const projectList = ref([]);
|
||||
const contractorList = ref([]);
|
||||
|
||||
const deptTableRef = ref<ElTableInstance>();
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
@ -180,6 +208,11 @@ const initFormData: DeptForm = {
|
||||
leader: undefined,
|
||||
phone: undefined,
|
||||
email: undefined,
|
||||
isShow: undefined,
|
||||
deptType: undefined,
|
||||
projectId: undefined,
|
||||
contractorId: undefined,
|
||||
rowProjectId: undefined,
|
||||
status: '0'
|
||||
};
|
||||
const initData: PageData<DeptForm, DeptQuery> = {
|
||||
@ -189,7 +222,9 @@ const initData: PageData<DeptForm, DeptQuery> = {
|
||||
pageSize: 10,
|
||||
deptName: undefined,
|
||||
deptCategory: undefined,
|
||||
status: undefined
|
||||
status: undefined,
|
||||
isShow: undefined,
|
||||
deptType: undefined
|
||||
},
|
||||
rules: {
|
||||
parentId: [{ required: true, message: '上级部门不能为空', trigger: 'blur' }],
|
||||
@ -230,6 +265,8 @@ const cancel = () => {
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
projectList.value = [];
|
||||
contractorList.value = [];
|
||||
deptFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
@ -240,6 +277,7 @@ const handleQuery = () => {
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
projectList.value = [];
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
@ -259,6 +297,7 @@ const toggleExpandAll = (data: DeptVO[], status: boolean) => {
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = async (row?: DeptVO) => {
|
||||
reset();
|
||||
form.value.rowProjectId = row?.projectId;
|
||||
const res = await listDept();
|
||||
const data = proxy?.handleTree<DeptOptionsType>(res.data, 'deptId');
|
||||
if (data) {
|
||||
@ -274,10 +313,14 @@ const handleAdd = async (row?: DeptVO) => {
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: DeptVO) => {
|
||||
reset();
|
||||
form.value.rowProjectId = row?.projectId;
|
||||
//查询当前部门所有用户
|
||||
getDeptAllUser(row.deptId);
|
||||
|
||||
const res = await getDept(row.deptId);
|
||||
form.value = res.data;
|
||||
projectList.value = res.data.projectList;
|
||||
contractorList.value = res.data.contractorList;
|
||||
const response = await listDeptExcludeChild(row.deptId);
|
||||
const data = proxy?.handleTree<DeptOptionsType>(response.data, 'deptId');
|
||||
if (data) {
|
||||
@ -294,6 +337,17 @@ const handleUpdate = async (row: DeptVO) => {
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改部门';
|
||||
};
|
||||
|
||||
const changeProject = async (val: any) => {
|
||||
if (val == '3' && (!projectList.value || !projectList.value.length)) {
|
||||
const res = await getDeptList();
|
||||
projectList.value = res.data;
|
||||
} else if (val == '4' && (!contractorList.value || !contractorList.value.length)) {
|
||||
const res = await optionProjectSelect(form.value.rowProjectId);
|
||||
contractorList.value = res;
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
deptFormRef.value?.validate(async (valid: boolean) => {
|
||||
|
@ -35,72 +35,110 @@
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
|
||||
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
|
||||
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" />
|
||||
<el-table-column label="显示顺序" prop="roleSort" width="100" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column fixed="right" label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column v-if="false" label="角色编号" prop="roleId" width="120" />
|
||||
<el-table-column label="角色名称" prop="roleName" :show-overflow-tooltip="true" width="150" />
|
||||
<el-table-column label="权限字符" prop="roleKey" :show-overflow-tooltip="true" width="200" />
|
||||
<el-table-column label="显示顺序" prop="roleSort" width="100" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
<el-table-column fixed="right" label="操作" width="180">
|
||||
<template #default="scope">
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="修改" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="删除" placement="top">
|
||||
<el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="数据权限" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="scope.row.roleId !== 1" content="分配用户" placement="top">
|
||||
<el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
||||
<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="所属部门" prop="deptId">
|
||||
<el-cascader
|
||||
:options="deptOptions"
|
||||
v-model="form.deptId"
|
||||
placeholder="请选择所属部门"
|
||||
clearable
|
||||
filterable
|
||||
:show-all-levels="false"
|
||||
:props="{ value: 'id', emitPath: false, checkStrictly: true }"
|
||||
@change=""
|
||||
>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="form.roleName" placeholder="请输入角色名称" />
|
||||
</el-form-item>
|
||||
@ -124,7 +162,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单权限">
|
||||
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand(Boolean($event), 'menu')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
@ -138,6 +176,9 @@
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
></el-tree>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否为特殊角色">
|
||||
<el-switch v-model="form.isSpecial" active-value="1" inactive-value="0" active-text="是" inactive-text="否"> </el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
@ -165,7 +206,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.dataScope === '2'" label="数据权限">
|
||||
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand(Boolean($event), 'dept')">展开/折叠</el-checkbox>
|
||||
<el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">全选/全不选</el-checkbox>
|
||||
<el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">父子联动</el-checkbox>
|
||||
<el-tree
|
||||
@ -196,6 +237,8 @@ import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updat
|
||||
import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
|
||||
import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
|
||||
import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
|
||||
import api, { uploadCertList } from '@/api/system/user';
|
||||
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@ -214,8 +257,11 @@ const menuExpand = ref(false);
|
||||
const menuNodeAll = ref(false);
|
||||
const deptExpand = ref(true);
|
||||
const deptNodeAll = ref(false);
|
||||
const deptOptions = ref<DeptTreeOption[]>([]);
|
||||
const deptOptions = ref<DeptTreeVO[]>([]);
|
||||
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
|
||||
|
||||
const openDataScope = ref(false);
|
||||
const deptName = ref('');
|
||||
|
||||
/** 数据范围选项*/
|
||||
const dataScopeOptions = ref([
|
||||
@ -232,6 +278,7 @@ const roleFormRef = ref<ElFormInstance>();
|
||||
const dataScopeRef = ref<ElFormInstance>();
|
||||
const menuRef = ref<ElTreeInstance>();
|
||||
const deptRef = ref<ElTreeInstance>();
|
||||
const deptTreeRef = ref<ElTreeInstance>();
|
||||
|
||||
const initForm: RoleForm = {
|
||||
roleId: undefined,
|
||||
@ -244,6 +291,8 @@ const initForm: RoleForm = {
|
||||
remark: '',
|
||||
dataScope: '1',
|
||||
menuIds: [],
|
||||
deptId: '',
|
||||
isSpecial: null,
|
||||
deptIds: []
|
||||
};
|
||||
|
||||
@ -254,6 +303,7 @@ const data = reactive<PageData<RoleForm, RoleQuery>>({
|
||||
pageSize: 10,
|
||||
roleName: '',
|
||||
roleKey: '',
|
||||
deptId: '',
|
||||
status: ''
|
||||
},
|
||||
rules: {
|
||||
@ -269,6 +319,18 @@ const dialog = reactive<DialogOption>({
|
||||
title: ''
|
||||
});
|
||||
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
|
||||
/** 节点单击事件 */
|
||||
const handleNodeClick = (data: DeptVO) => {
|
||||
queryParams.value.deptId = data.id as string;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询角色列表
|
||||
*/
|
||||
@ -293,6 +355,9 @@ const handleQuery = () => {
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.deptId = undefined;
|
||||
deptTreeRef.value?.setCurrentKey(undefined);
|
||||
handleQuery();
|
||||
};
|
||||
/**删除按钮操作 */
|
||||
@ -496,8 +561,28 @@ const cancelDataScope = () => {
|
||||
form.value = { ...initForm };
|
||||
openDataScope.value = false;
|
||||
};
|
||||
/** 查询部门下拉树结构 */
|
||||
const getDeptTree = async () => {
|
||||
const res = await api.deptTreeSelect({ isShow: '1' });
|
||||
deptOptions.value = res.data;
|
||||
enabledDeptOptions.value = filterDisabledDept(res.data);
|
||||
};
|
||||
|
||||
/** 过滤禁用的部门 */
|
||||
const filterDisabledDept = (deptList: DeptTreeVO[]) => {
|
||||
return deptList.filter((dept) => {
|
||||
if (dept.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (dept.children && dept.children.length) {
|
||||
dept.children = filterDisabledDept(dept.children);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDeptTree(); // 初始化部门数据
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
@ -317,7 +317,7 @@ import { RoleVO } from '@/api/system/role/types';
|
||||
import { PostVO } from '@/api/system/post/types';
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
import { to } from 'await-to-js';
|
||||
import { optionselect } from '@/api/system/post';
|
||||
import { getRoleList, optionselect } from '@/api/system/post';
|
||||
import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue';
|
||||
|
||||
const router = useRouter();
|
||||
@ -430,6 +430,7 @@ const initData: PageData<UserForm, UserQuery> = {
|
||||
}
|
||||
],
|
||||
phonenumber: [
|
||||
{ required: true, message: '请输入手机号码', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
@ -469,7 +470,7 @@ const getList = async () => {
|
||||
|
||||
/** 查询部门下拉树结构 */
|
||||
const getDeptTree = async () => {
|
||||
const res = await api.deptTreeSelect();
|
||||
const res = await api.deptTreeSelect({ isShow: '1' });
|
||||
deptOptions.value = res.data;
|
||||
enabledDeptOptions.value = filterDisabledDept(res.data);
|
||||
};
|
||||
@ -623,7 +624,6 @@ const handleAdd = async () => {
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增用户';
|
||||
postOptions.value = data.posts;
|
||||
roleOptions.value = data.roles;
|
||||
form.value.password = initPassword.value.toString();
|
||||
};
|
||||
|
||||
@ -638,7 +638,7 @@ const handleUpdate = async (row?: UserForm) => {
|
||||
postOptions.value = data.posts;
|
||||
roleOptions.value = data.roles;
|
||||
form.value.postIds = data.postIds;
|
||||
form.value.roleIds = data.roleIds;
|
||||
form.value.roleIds = data.user.roleIds;
|
||||
form.value.password = '';
|
||||
};
|
||||
|
||||
@ -682,8 +682,11 @@ onMounted(() => {
|
||||
|
||||
async function handleDeptChange(value: number | string) {
|
||||
const response = await optionselect(value);
|
||||
const roleList = await getRoleList(value);
|
||||
roleOptions.value = roleList.data;
|
||||
postOptions.value = response.data;
|
||||
form.value.postIds = [];
|
||||
form.value.roleIds = [];
|
||||
}
|
||||
|
||||
const shuttleVisible = ref(false);
|
||||
|
@ -18,7 +18,7 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['workflow:category:add']">新增</el-button>
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['workflows:category:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
@ -41,13 +41,13 @@
|
||||
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:category:edit']" />
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflows:category:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['workflow:category:add']" />
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['workflows:category:add']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:category:remove']" />
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflows:category:remove']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -59,7 +59,7 @@
|
||||
<el-tree-select
|
||||
v-model="form.parentId"
|
||||
:data="categoryOptions"
|
||||
:props="{ value: 'categoryId', label: 'categoryName', children: 'children' } as any"
|
||||
:props="{ value: 'categoryId', label: 'categoryName', children: 'children' }"
|
||||
value-key="categoryId"
|
||||
placeholder="请选择上级分类"
|
||||
check-strictly
|
@ -22,10 +22,10 @@
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['workflow:leave:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
<el-button v-hasPermi="['workflows:leave:add']" type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button v-hasPermi="['workflow:leave:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
<el-button v-hasPermi="['workflows:leave:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@ -60,12 +60,12 @@
|
||||
<template #default="scope">
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5" v-if="scope.row.status === 'draft' || scope.row.status === 'cancel' || scope.row.status === 'back'">
|
||||
<el-button v-hasPermi="['workflow:leave:edit']" size="small" type="primary" icon="Edit" @click="handleUpdate(scope.row)"
|
||||
<el-button v-hasPermi="['workflows:leave:edit']" size="small" type="primary" icon="Edit" @click="handleUpdate(scope.row)"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5" v-if="scope.row.status === 'draft' || scope.row.status === 'cancel' || scope.row.status === 'back'">
|
||||
<el-button v-hasPermi="['workflow:leave:remove']" size="small" type="primary" icon="Delete" @click="handleDelete(scope.row)"
|
||||
<el-button v-hasPermi="['workflows:leave:remove']" size="small" type="primary" icon="Delete" @click="handleDelete(scope.row)"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
@ -167,7 +167,7 @@ const handleSelectionChange = (selection: LeaveVO[]) => {
|
||||
const handleAdd = () => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/leaveEdit/index`,
|
||||
path: `/test/leaveEdit/index`,
|
||||
query: {
|
||||
type: 'add'
|
||||
}
|
||||
@ -178,7 +178,7 @@ const handleAdd = () => {
|
||||
const handleUpdate = (row?: LeaveVO) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/leaveEdit/index`,
|
||||
path: `/test/leaveEdit/index`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
@ -190,7 +190,7 @@ const handleUpdate = (row?: LeaveVO) => {
|
||||
const handleView = (row?: LeaveVO) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/workflow/leaveEdit/index`,
|
||||
path: `/test/leaveEdit/index`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'view'
|
@ -30,7 +30,7 @@ const open = async (definitionId, disabled) => {
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
const obj = { path: '/workflow/processDefinition', query: { activeName: proxy.$route.query.activeName } };
|
||||
const obj = { path: '/test/test/processDefinition', query: { activeName: proxy.$route.query.activeName } };
|
||||
proxy.$tab.closeOpenPage(obj);
|
||||
}
|
||||
|
@ -10,13 +10,13 @@
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="categoryOptions"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
></el-tree>
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
@ -132,7 +132,7 @@
|
||||
<el-tree-select
|
||||
v-model="selectCategory"
|
||||
:data="categoryOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' } as any"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }"
|
||||
filterable
|
||||
value-key="id"
|
||||
:render-after-expand="false"
|
||||
@ -164,7 +164,7 @@
|
||||
<el-tree-select
|
||||
v-model="form.category"
|
||||
:data="categoryOptions"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' } as any"
|
||||
:props="{ value: 'id', label: 'label', children: 'children' }"
|
||||
filterable
|
||||
value-key="id"
|
||||
:render-after-expand="false"
|
||||
@ -174,7 +174,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="流程编码" prop="flowCode">
|
||||
<el-input v-model="form.flowCode" placeholder="请输入流程编码" maxlength="20" show-word-limit>
|
||||
<template #prepend >{{ currentProject.id }}</template>
|
||||
<template #prepend>{{ currentProject.id }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程名称" prop="flowName">
|
||||
@ -442,7 +442,7 @@ const handlerImportDefinition = (data: UploadRequestOptions): XMLHttpRequest =>
|
||||
*/
|
||||
const design = async (row: FlowDefinitionVo) => {
|
||||
proxy.$router.push({
|
||||
path: `/workflow/design/index`,
|
||||
path: `/test/design/index`,
|
||||
query: {
|
||||
definitionId: row.id,
|
||||
disabled: false,
|
||||
@ -456,13 +456,10 @@ const design = async (row: FlowDefinitionVo) => {
|
||||
* @param row
|
||||
*/
|
||||
const designView = async (row: FlowDefinitionVo) => {
|
||||
proxy.$router.push({
|
||||
path: `/workflow/design/index`,
|
||||
query: {
|
||||
definitionId: row.id,
|
||||
disabled: true,
|
||||
activeName: activeName.value
|
||||
}
|
||||
proxy.$tab.openPage(`/test/design/index`, '', {
|
||||
definitionId: row.id,
|
||||
disabled: true,
|
||||
activeName: activeName.value
|
||||
});
|
||||
};
|
||||
/** 表单重置 */
|
||||
@ -532,4 +529,17 @@ const handleCopyDef = async (row: FlowDefinitionVo) => {
|
||||
const handleExportDef = () => {
|
||||
proxy?.download(`/workflow/definition/exportDef/${ids.value[0]}`, {}, `${flowCodeList.value[0]}.json`);
|
||||
};
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value.id,
|
||||
(nid, oid) => {
|
||||
queryParams.value.projectId = nid;
|
||||
form.value.projectId = nid;
|
||||
getList();
|
||||
}
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
</script>
|
@ -10,7 +10,7 @@
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="categoryOptions"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
@ -29,18 +29,20 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
},"/warm-flow-ui": {
|
||||
target: 'http://192.168.110.119:8899',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
},"/warm-flow": {
|
||||
},
|
||||
'/warm-flow-ui': {
|
||||
target: 'http://192.168.110.119:8899',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
},
|
||||
"/workflow": {
|
||||
'/warm-flow': {
|
||||
target: 'http://192.168.110.119:8899',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
|
||||
},
|
||||
'/workflow': {
|
||||
target: 'http://192.168.110.119:8899',
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
|
Reference in New Issue
Block a user