250213
基本完成三品PLM变更管理相关笔记
3
.obsidian/community-plugins.json
vendored
@ -4,5 +4,6 @@
|
||||
"hidden-folder-obsidian",
|
||||
"update-relative-links",
|
||||
"better-export-pdf",
|
||||
"easy-typing-obsidian"
|
||||
"easy-typing-obsidian",
|
||||
"file-explorer-note-count"
|
||||
]
|
4
.obsidian/graph.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"collapse-filter": false,
|
||||
"search": "path:KMmpm ",
|
||||
"search": "path:SanPinPLM ",
|
||||
"showTags": false,
|
||||
"showAttachments": false,
|
||||
"hideUnresolved": false,
|
||||
@ -32,6 +32,6 @@
|
||||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 1.0000000000000195,
|
||||
"scale": 0.7514342322277608,
|
||||
"close": true
|
||||
}
|
8
.obsidian/plugins/file-explorer-note-count/data.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"showAllNumbers": true,
|
||||
"filterList": [
|
||||
"md"
|
||||
],
|
||||
"blacklist": false,
|
||||
"addRootFolder": false
|
||||
}
|
970
.obsidian/plugins/file-explorer-note-count/main.js
vendored
Normal file
@ -0,0 +1,970 @@
|
||||
'use strict';
|
||||
|
||||
var obsidian = require('obsidian');
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
function __awaiter(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
}
|
||||
|
||||
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
||||
var e = new Error(message);
|
||||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
||||
};
|
||||
|
||||
// 'path' module extracted from Node.js v8.11.1 (only the posix part)
|
||||
|
||||
function assertPath(path) {
|
||||
if (typeof path !== 'string') {
|
||||
throw new TypeError('Path must be a string. Received ' + JSON.stringify(path));
|
||||
}
|
||||
}
|
||||
|
||||
// Resolves . and .. elements in a path with directory names
|
||||
function normalizeStringPosix(path, allowAboveRoot) {
|
||||
var res = '';
|
||||
var lastSegmentLength = 0;
|
||||
var lastSlash = -1;
|
||||
var dots = 0;
|
||||
var code;
|
||||
for (var i = 0; i <= path.length; ++i) {
|
||||
if (i < path.length)
|
||||
code = path.charCodeAt(i);
|
||||
else if (code === 47 /*/*/)
|
||||
break;
|
||||
else
|
||||
code = 47 /*/*/;
|
||||
if (code === 47 /*/*/) {
|
||||
if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) {
|
||||
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 /*.*/ || res.charCodeAt(res.length - 2) !== 46 /*.*/) {
|
||||
if (res.length > 2) {
|
||||
var lastSlashIndex = res.lastIndexOf('/');
|
||||
if (lastSlashIndex !== res.length - 1) {
|
||||
if (lastSlashIndex === -1) {
|
||||
res = '';
|
||||
lastSegmentLength = 0;
|
||||
} else {
|
||||
res = res.slice(0, lastSlashIndex);
|
||||
lastSegmentLength = res.length - 1 - res.lastIndexOf('/');
|
||||
}
|
||||
lastSlash = i;
|
||||
dots = 0;
|
||||
continue;
|
||||
}
|
||||
} else if (res.length === 2 || res.length === 1) {
|
||||
res = '';
|
||||
lastSegmentLength = 0;
|
||||
lastSlash = i;
|
||||
dots = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (allowAboveRoot) {
|
||||
if (res.length > 0)
|
||||
res += '/..';
|
||||
else
|
||||
res = '..';
|
||||
lastSegmentLength = 2;
|
||||
}
|
||||
} else {
|
||||
if (res.length > 0)
|
||||
res += '/' + path.slice(lastSlash + 1, i);
|
||||
else
|
||||
res = path.slice(lastSlash + 1, i);
|
||||
lastSegmentLength = i - lastSlash - 1;
|
||||
}
|
||||
lastSlash = i;
|
||||
dots = 0;
|
||||
} else if (code === 46 /*.*/ && dots !== -1) {
|
||||
++dots;
|
||||
} else {
|
||||
dots = -1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function _format(sep, pathObject) {
|
||||
var dir = pathObject.dir || pathObject.root;
|
||||
var base = pathObject.base || (pathObject.name || '') + (pathObject.ext || '');
|
||||
if (!dir) {
|
||||
return base;
|
||||
}
|
||||
if (dir === pathObject.root) {
|
||||
return dir + base;
|
||||
}
|
||||
return dir + sep + base;
|
||||
}
|
||||
|
||||
var posix = {
|
||||
// path.resolve([from ...], to)
|
||||
resolve: function resolve() {
|
||||
var resolvedPath = '';
|
||||
var resolvedAbsolute = false;
|
||||
var cwd;
|
||||
|
||||
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
||||
var path;
|
||||
if (i >= 0)
|
||||
path = arguments[i];
|
||||
else {
|
||||
if (cwd === undefined)
|
||||
cwd = process.cwd();
|
||||
path = cwd;
|
||||
}
|
||||
|
||||
assertPath(path);
|
||||
|
||||
// Skip empty entries
|
||||
if (path.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
resolvedPath = path + '/' + resolvedPath;
|
||||
resolvedAbsolute = path.charCodeAt(0) === 47 /*/*/;
|
||||
}
|
||||
|
||||
// At this point the path should be resolved to a full absolute path, but
|
||||
// handle relative paths to be safe (might happen when process.cwd() fails)
|
||||
|
||||
// Normalize the path
|
||||
resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute);
|
||||
|
||||
if (resolvedAbsolute) {
|
||||
if (resolvedPath.length > 0)
|
||||
return '/' + resolvedPath;
|
||||
else
|
||||
return '/';
|
||||
} else if (resolvedPath.length > 0) {
|
||||
return resolvedPath;
|
||||
} else {
|
||||
return '.';
|
||||
}
|
||||
},
|
||||
|
||||
normalize: function normalize(path) {
|
||||
assertPath(path);
|
||||
|
||||
if (path.length === 0) return '.';
|
||||
|
||||
var isAbsolute = path.charCodeAt(0) === 47 /*/*/;
|
||||
var trailingSeparator = path.charCodeAt(path.length - 1) === 47 /*/*/;
|
||||
|
||||
// Normalize the path
|
||||
path = normalizeStringPosix(path, !isAbsolute);
|
||||
|
||||
if (path.length === 0 && !isAbsolute) path = '.';
|
||||
if (path.length > 0 && trailingSeparator) path += '/';
|
||||
|
||||
if (isAbsolute) return '/' + path;
|
||||
return path;
|
||||
},
|
||||
|
||||
isAbsolute: function isAbsolute(path) {
|
||||
assertPath(path);
|
||||
return path.length > 0 && path.charCodeAt(0) === 47 /*/*/;
|
||||
},
|
||||
|
||||
join: function join() {
|
||||
if (arguments.length === 0)
|
||||
return '.';
|
||||
var joined;
|
||||
for (var i = 0; i < arguments.length; ++i) {
|
||||
var arg = arguments[i];
|
||||
assertPath(arg);
|
||||
if (arg.length > 0) {
|
||||
if (joined === undefined)
|
||||
joined = arg;
|
||||
else
|
||||
joined += '/' + arg;
|
||||
}
|
||||
}
|
||||
if (joined === undefined)
|
||||
return '.';
|
||||
return posix.normalize(joined);
|
||||
},
|
||||
|
||||
relative: function relative(from, to) {
|
||||
assertPath(from);
|
||||
assertPath(to);
|
||||
|
||||
if (from === to) return '';
|
||||
|
||||
from = posix.resolve(from);
|
||||
to = posix.resolve(to);
|
||||
|
||||
if (from === to) return '';
|
||||
|
||||
// Trim any leading backslashes
|
||||
var fromStart = 1;
|
||||
for (; fromStart < from.length; ++fromStart) {
|
||||
if (from.charCodeAt(fromStart) !== 47 /*/*/)
|
||||
break;
|
||||
}
|
||||
var fromEnd = from.length;
|
||||
var fromLen = fromEnd - fromStart;
|
||||
|
||||
// Trim any leading backslashes
|
||||
var toStart = 1;
|
||||
for (; toStart < to.length; ++toStart) {
|
||||
if (to.charCodeAt(toStart) !== 47 /*/*/)
|
||||
break;
|
||||
}
|
||||
var toEnd = to.length;
|
||||
var toLen = toEnd - toStart;
|
||||
|
||||
// Compare paths to find the longest common path from root
|
||||
var length = fromLen < toLen ? fromLen : toLen;
|
||||
var lastCommonSep = -1;
|
||||
var i = 0;
|
||||
for (; i <= length; ++i) {
|
||||
if (i === length) {
|
||||
if (toLen > length) {
|
||||
if (to.charCodeAt(toStart + i) === 47 /*/*/) {
|
||||
// We get here if `from` is the exact base path for `to`.
|
||||
// For example: from='/foo/bar'; to='/foo/bar/baz'
|
||||
return to.slice(toStart + i + 1);
|
||||
} else if (i === 0) {
|
||||
// We get here if `from` is the root
|
||||
// For example: from='/'; to='/foo'
|
||||
return to.slice(toStart + i);
|
||||
}
|
||||
} else if (fromLen > length) {
|
||||
if (from.charCodeAt(fromStart + i) === 47 /*/*/) {
|
||||
// We get here if `to` is the exact base path for `from`.
|
||||
// For example: from='/foo/bar/baz'; to='/foo/bar'
|
||||
lastCommonSep = i;
|
||||
} else if (i === 0) {
|
||||
// We get here if `to` is the root.
|
||||
// For example: from='/foo'; to='/'
|
||||
lastCommonSep = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
var fromCode = from.charCodeAt(fromStart + i);
|
||||
var toCode = to.charCodeAt(toStart + i);
|
||||
if (fromCode !== toCode)
|
||||
break;
|
||||
else if (fromCode === 47 /*/*/)
|
||||
lastCommonSep = i;
|
||||
}
|
||||
|
||||
var out = '';
|
||||
// Generate the relative path based on the path difference between `to`
|
||||
// and `from`
|
||||
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
|
||||
if (i === fromEnd || from.charCodeAt(i) === 47 /*/*/) {
|
||||
if (out.length === 0)
|
||||
out += '..';
|
||||
else
|
||||
out += '/..';
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, append the rest of the destination (`to`) path that comes after
|
||||
// the common path parts
|
||||
if (out.length > 0)
|
||||
return out + to.slice(toStart + lastCommonSep);
|
||||
else {
|
||||
toStart += lastCommonSep;
|
||||
if (to.charCodeAt(toStart) === 47 /*/*/)
|
||||
++toStart;
|
||||
return to.slice(toStart);
|
||||
}
|
||||
},
|
||||
|
||||
_makeLong: function _makeLong(path) {
|
||||
return path;
|
||||
},
|
||||
|
||||
dirname: function dirname(path) {
|
||||
assertPath(path);
|
||||
if (path.length === 0) return '.';
|
||||
var code = path.charCodeAt(0);
|
||||
var hasRoot = code === 47 /*/*/;
|
||||
var end = -1;
|
||||
var matchedSlash = true;
|
||||
for (var i = path.length - 1; i >= 1; --i) {
|
||||
code = path.charCodeAt(i);
|
||||
if (code === 47 /*/*/) {
|
||||
if (!matchedSlash) {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// We saw the first non-path separator
|
||||
matchedSlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (end === -1) return hasRoot ? '/' : '.';
|
||||
if (hasRoot && end === 1) return '//';
|
||||
return path.slice(0, end);
|
||||
},
|
||||
|
||||
basename: function basename(path, ext) {
|
||||
if (ext !== undefined && typeof ext !== 'string') throw new TypeError('"ext" argument must be a string');
|
||||
assertPath(path);
|
||||
|
||||
var start = 0;
|
||||
var end = -1;
|
||||
var matchedSlash = true;
|
||||
var i;
|
||||
|
||||
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
|
||||
if (ext.length === path.length && ext === path) return '';
|
||||
var extIdx = ext.length - 1;
|
||||
var firstNonSlashEnd = -1;
|
||||
for (i = path.length - 1; i >= 0; --i) {
|
||||
var code = path.charCodeAt(i);
|
||||
if (code === 47 /*/*/) {
|
||||
// If we reached a path separator that was not part of a set of path
|
||||
// separators at the end of the string, stop now
|
||||
if (!matchedSlash) {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (firstNonSlashEnd === -1) {
|
||||
// We saw the first non-path separator, remember this index in case
|
||||
// we need it if the extension ends up not matching
|
||||
matchedSlash = false;
|
||||
firstNonSlashEnd = i + 1;
|
||||
}
|
||||
if (extIdx >= 0) {
|
||||
// Try to match the explicit extension
|
||||
if (code === ext.charCodeAt(extIdx)) {
|
||||
if (--extIdx === -1) {
|
||||
// We matched the extension, so mark this as the end of our path
|
||||
// component
|
||||
end = i;
|
||||
}
|
||||
} else {
|
||||
// Extension does not match, so our result is the entire path
|
||||
// component
|
||||
extIdx = -1;
|
||||
end = firstNonSlashEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start === end) end = firstNonSlashEnd;else if (end === -1) end = path.length;
|
||||
return path.slice(start, end);
|
||||
} else {
|
||||
for (i = path.length - 1; i >= 0; --i) {
|
||||
if (path.charCodeAt(i) === 47 /*/*/) {
|
||||
// If we reached a path separator that was not part of a set of path
|
||||
// separators at the end of the string, stop now
|
||||
if (!matchedSlash) {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
} else if (end === -1) {
|
||||
// We saw the first non-path separator, mark this as the end of our
|
||||
// path component
|
||||
matchedSlash = false;
|
||||
end = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (end === -1) return '';
|
||||
return path.slice(start, end);
|
||||
}
|
||||
},
|
||||
|
||||
extname: function extname(path) {
|
||||
assertPath(path);
|
||||
var startDot = -1;
|
||||
var startPart = 0;
|
||||
var end = -1;
|
||||
var matchedSlash = true;
|
||||
// Track the state of characters (if any) we see before our first dot and
|
||||
// after any path separator we find
|
||||
var preDotState = 0;
|
||||
for (var i = path.length - 1; i >= 0; --i) {
|
||||
var code = path.charCodeAt(i);
|
||||
if (code === 47 /*/*/) {
|
||||
// If we reached a path separator that was not part of a set of path
|
||||
// separators at the end of the string, stop now
|
||||
if (!matchedSlash) {
|
||||
startPart = i + 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (end === -1) {
|
||||
// We saw the first non-path separator, mark this as the end of our
|
||||
// extension
|
||||
matchedSlash = false;
|
||||
end = i + 1;
|
||||
}
|
||||
if (code === 46 /*.*/) {
|
||||
// If this is our first dot, mark it as the start of our extension
|
||||
if (startDot === -1)
|
||||
startDot = i;
|
||||
else if (preDotState !== 1)
|
||||
preDotState = 1;
|
||||
} else if (startDot !== -1) {
|
||||
// We saw a non-dot and non-path separator before our dot, so we should
|
||||
// have a good chance at having a non-empty extension
|
||||
preDotState = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (startDot === -1 || end === -1 ||
|
||||
// We saw a non-dot character immediately before the dot
|
||||
preDotState === 0 ||
|
||||
// The (right-most) trimmed path component is exactly '..'
|
||||
preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
||||
return '';
|
||||
}
|
||||
return path.slice(startDot, end);
|
||||
},
|
||||
|
||||
format: function format(pathObject) {
|
||||
if (pathObject === null || typeof pathObject !== 'object') {
|
||||
throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
|
||||
}
|
||||
return _format('/', pathObject);
|
||||
},
|
||||
|
||||
parse: function parse(path) {
|
||||
assertPath(path);
|
||||
|
||||
var ret = { root: '', dir: '', base: '', ext: '', name: '' };
|
||||
if (path.length === 0) return ret;
|
||||
var code = path.charCodeAt(0);
|
||||
var isAbsolute = code === 47 /*/*/;
|
||||
var start;
|
||||
if (isAbsolute) {
|
||||
ret.root = '/';
|
||||
start = 1;
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
var startDot = -1;
|
||||
var startPart = 0;
|
||||
var end = -1;
|
||||
var matchedSlash = true;
|
||||
var i = path.length - 1;
|
||||
|
||||
// Track the state of characters (if any) we see before our first dot and
|
||||
// after any path separator we find
|
||||
var preDotState = 0;
|
||||
|
||||
// Get non-dir info
|
||||
for (; i >= start; --i) {
|
||||
code = path.charCodeAt(i);
|
||||
if (code === 47 /*/*/) {
|
||||
// If we reached a path separator that was not part of a set of path
|
||||
// separators at the end of the string, stop now
|
||||
if (!matchedSlash) {
|
||||
startPart = i + 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (end === -1) {
|
||||
// We saw the first non-path separator, mark this as the end of our
|
||||
// extension
|
||||
matchedSlash = false;
|
||||
end = i + 1;
|
||||
}
|
||||
if (code === 46 /*.*/) {
|
||||
// If this is our first dot, mark it as the start of our extension
|
||||
if (startDot === -1) startDot = i;else if (preDotState !== 1) preDotState = 1;
|
||||
} else if (startDot !== -1) {
|
||||
// We saw a non-dot and non-path separator before our dot, so we should
|
||||
// have a good chance at having a non-empty extension
|
||||
preDotState = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (startDot === -1 || end === -1 ||
|
||||
// We saw a non-dot character immediately before the dot
|
||||
preDotState === 0 ||
|
||||
// The (right-most) trimmed path component is exactly '..'
|
||||
preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
||||
if (end !== -1) {
|
||||
if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);else ret.base = ret.name = path.slice(startPart, end);
|
||||
}
|
||||
} else {
|
||||
if (startPart === 0 && isAbsolute) {
|
||||
ret.name = path.slice(1, startDot);
|
||||
ret.base = path.slice(1, end);
|
||||
} else {
|
||||
ret.name = path.slice(startPart, startDot);
|
||||
ret.base = path.slice(startPart, end);
|
||||
}
|
||||
ret.ext = path.slice(startDot, end);
|
||||
}
|
||||
|
||||
if (startPart > 0) ret.dir = path.slice(0, startPart - 1);else if (isAbsolute) ret.dir = '/';
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
sep: '/',
|
||||
delimiter: ':',
|
||||
win32: null,
|
||||
posix: null
|
||||
};
|
||||
|
||||
posix.posix = posix;
|
||||
|
||||
var pathBrowserify = posix;
|
||||
|
||||
const withSubfolderClass = 'oz-with-subfolder';
|
||||
const showAllNumbersClass = 'oz-show-all-num';
|
||||
const isFolder = (item) => item.file instanceof obsidian.TFolder;
|
||||
const iterateItems = (items, callback) => {
|
||||
for (const key in items) {
|
||||
if (!Object.prototype.hasOwnProperty.call(items, key))
|
||||
continue;
|
||||
callback(items[key]);
|
||||
}
|
||||
};
|
||||
const getParentPath = (src) => {
|
||||
if (src === '/')
|
||||
return null;
|
||||
const path = pathBrowserify.dirname(src);
|
||||
if (path === '.')
|
||||
return '/';
|
||||
else
|
||||
return path;
|
||||
};
|
||||
const equals = (arr1, arr2) => {
|
||||
// if the other array is a falsy value, return
|
||||
if (!Array.isArray(arr1) || !Array.isArray(arr2))
|
||||
return false;
|
||||
// compare lengths - can save a lot of time
|
||||
if (arr1.length != arr2.length)
|
||||
return false;
|
||||
return arr1.every((v, i) => v === arr2[i]);
|
||||
};
|
||||
const isParent = (parent, child) => {
|
||||
if (child === parent)
|
||||
return false;
|
||||
if (parent === '/')
|
||||
parent = '';
|
||||
if (child === '/')
|
||||
child = '';
|
||||
const parentTokens = parent.split('/').filter((i) => i.length);
|
||||
return parentTokens.every((t, i) => child.split('/')[i] === t);
|
||||
};
|
||||
// Helper to play with the File Explorer (if exists)
|
||||
const doWithFileExplorer = (plugin, callback) => {
|
||||
let leaves;
|
||||
let count = 0;
|
||||
const tryGetView = () => {
|
||||
leaves = plugin.app.workspace.getLeavesOfType('file-explorer');
|
||||
if (leaves.length === 0) {
|
||||
if (count++ > 5)
|
||||
console.error('failed to get file-explorer');
|
||||
else {
|
||||
console.log('file-explorer not found, retrying...');
|
||||
setTimeout(tryGetView, 500);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (leaves.length > 1)
|
||||
console.warn('more then one file-explorer');
|
||||
callback(leaves[0].view);
|
||||
}
|
||||
};
|
||||
tryGetView();
|
||||
};
|
||||
|
||||
function around(obj, factories) {
|
||||
const removers = Object.keys(factories).map(key => around1(obj, key, factories[key]));
|
||||
return removers.length === 1 ? removers[0] : function () { removers.forEach(r => r()); };
|
||||
}
|
||||
function around1(obj, method, createWrapper) {
|
||||
const original = obj[method], hadOwn = obj.hasOwnProperty(method);
|
||||
let current = createWrapper(original);
|
||||
// Let our wrapper inherit static props from the wrapping method,
|
||||
// and the wrapping method, props from the original method
|
||||
if (original)
|
||||
Object.setPrototypeOf(current, original);
|
||||
Object.setPrototypeOf(wrapper, current);
|
||||
obj[method] = wrapper;
|
||||
// Return a callback to allow safe removal
|
||||
return remove;
|
||||
function wrapper(...args) {
|
||||
// If we have been deactivated and are no longer wrapped, remove ourselves
|
||||
if (current === original && obj[method] === wrapper)
|
||||
remove();
|
||||
return current.apply(this, args);
|
||||
}
|
||||
function remove() {
|
||||
// If no other patches, just do a direct removal
|
||||
if (obj[method] === wrapper) {
|
||||
if (hadOwn)
|
||||
obj[method] = original;
|
||||
else
|
||||
delete obj[method];
|
||||
}
|
||||
if (current === original)
|
||||
return;
|
||||
// Else pass future calls through, and remove wrapper from the prototype chain
|
||||
current = original;
|
||||
Object.setPrototypeOf(wrapper, original || Function);
|
||||
}
|
||||
}
|
||||
|
||||
const countFolderChildren = (folder, filter) => {
|
||||
let count = 0;
|
||||
for (const af of folder.children) {
|
||||
if (filter(af))
|
||||
count++;
|
||||
if (af instanceof obsidian.TFolder)
|
||||
count += countFolderChildren(af, filter);
|
||||
}
|
||||
return count;
|
||||
};
|
||||
/** filter out all path that is the parent of existing path */
|
||||
const filterParent = (pathList) => {
|
||||
const list = Array.from(pathList);
|
||||
list.sort();
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (i < list.length - 1 && (list[i] === list[i + 1] || isParent(list[i], list[i + 1]))) {
|
||||
list.shift();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return new Set(list);
|
||||
};
|
||||
/** get all parents and add to set if not exist */
|
||||
const getAllParents = (path, set) => {
|
||||
let parent = getParentPath(path);
|
||||
while (parent && !set.has(parent)) {
|
||||
set.add(parent);
|
||||
parent = getParentPath(parent);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Update folder count of target's parent
|
||||
*/
|
||||
const updateCount = (targetList, plugin) => {
|
||||
const set = filterParent(targetList);
|
||||
for (const path of targetList) {
|
||||
getAllParents(path, set);
|
||||
}
|
||||
// set count of path
|
||||
const { fileExplorer, fileFilter } = plugin;
|
||||
if (!fileExplorer) {
|
||||
console.error('fileExplorer missing');
|
||||
return;
|
||||
}
|
||||
for (const path of set) {
|
||||
// check if path available
|
||||
if (!fileExplorer.fileItems[path])
|
||||
continue;
|
||||
setCount(fileExplorer.fileItems[path], fileFilter);
|
||||
}
|
||||
// Update root separately
|
||||
if (plugin.rootFolderEl && plugin.settings.addRootFolder) {
|
||||
setupRootCount(plugin);
|
||||
}
|
||||
// empty waitingList
|
||||
targetList.length = 0;
|
||||
};
|
||||
const setupRootCount = (plugin) => {
|
||||
if (plugin.rootFolderEl) {
|
||||
let rootFolderElChildren = plugin.rootFolderEl.children;
|
||||
if (rootFolderElChildren && rootFolderElChildren.length > 0) {
|
||||
let totalCount = countFolderChildren(plugin.app.vault.getAbstractFileByPath('/'), plugin.fileFilter);
|
||||
rootFolderElChildren[0].setAttr('data-count', totalCount.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
const setupCount = (plugin, revert = false) => {
|
||||
if (!plugin.fileExplorer)
|
||||
throw new Error('fileExplorer not found');
|
||||
// For each setup, first setup the root folder
|
||||
plugin.setupRootFolder();
|
||||
setupRootCount(plugin);
|
||||
// Iterate other items and include new counts
|
||||
iterateItems(plugin.fileExplorer.fileItems, (item) => {
|
||||
if (!isFolder(item))
|
||||
return;
|
||||
if (revert)
|
||||
removeCount(item);
|
||||
else
|
||||
setCount(item, plugin.fileFilter);
|
||||
});
|
||||
};
|
||||
const setCount = (item, filter) => {
|
||||
// if (item.file.isRoot()) return;
|
||||
const count = countFolderChildren(item.file, filter);
|
||||
item.selfEl.dataset['count'] = count.toString();
|
||||
item.selfEl.toggleClass(withSubfolderClass, Array.isArray(item.file.children) && item.file.children.some((af) => af instanceof obsidian.TFolder));
|
||||
};
|
||||
const removeCount = (item) => {
|
||||
if (item.selfEl.dataset['count'])
|
||||
delete item.selfEl.dataset['count'];
|
||||
item.selfEl.removeClass(withSubfolderClass);
|
||||
};
|
||||
|
||||
class VaultHandler {
|
||||
get app() {
|
||||
return this.plugin.app;
|
||||
}
|
||||
get vault() {
|
||||
return this.plugin.app.vault;
|
||||
}
|
||||
constructor(plugin) {
|
||||
this.waitingList = [];
|
||||
this.update = obsidian.debounce(() => updateCount(this.waitingList, this.plugin), 500, true);
|
||||
this.handler = (...args) => {
|
||||
var _a;
|
||||
for (const arg of args) {
|
||||
const path = arg instanceof obsidian.TAbstractFile ? arg.path : arg;
|
||||
this.waitingList.push((_a = getParentPath(path)) !== null && _a !== void 0 ? _a : '/');
|
||||
}
|
||||
this.update();
|
||||
};
|
||||
this.registerVaultEvent = () => {
|
||||
this.plugin.registerEvent(this.vault.on('create', this.handler));
|
||||
this.plugin.registerEvent(this.vault.on('rename', this.handler));
|
||||
this.plugin.registerEvent(this.vault.on('delete', this.handler));
|
||||
};
|
||||
this.plugin = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS = {
|
||||
showAllNumbers: false,
|
||||
filterList: ['md'],
|
||||
blacklist: false,
|
||||
addRootFolder: false,
|
||||
};
|
||||
class FENoteCountSettingTab extends obsidian.PluginSettingTab {
|
||||
constructor(app, plugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
get showOnlyNoteValue() {
|
||||
const { settings } = this.plugin;
|
||||
return settings.blacklist === DEFAULT_SETTINGS.blacklist && equals(settings.filterList, DEFAULT_SETTINGS.filterList);
|
||||
}
|
||||
set showOnlyNoteValue(value) {
|
||||
const { blacklist, filterList } = DEFAULT_SETTINGS;
|
||||
this.plugin.settings.blacklist = blacklist;
|
||||
if (value) {
|
||||
// do deep copy
|
||||
this.plugin.settings.filterList = Array.from(filterList);
|
||||
}
|
||||
else {
|
||||
this.plugin.settings.filterList.length = 0;
|
||||
}
|
||||
}
|
||||
display() {
|
||||
let { containerEl } = this;
|
||||
containerEl.empty();
|
||||
containerEl.createEl('h2', {
|
||||
text: 'File Explorer Note Count Settings',
|
||||
});
|
||||
new obsidian.Setting(containerEl)
|
||||
.setName('Show All Numbers')
|
||||
.setDesc('Turn on this option if you want to see the number of notes even after you expand the collapsed folders')
|
||||
.addToggle((toggle) => toggle.setValue(this.plugin.settings.showAllNumbers).onChange((value) => {
|
||||
document.body.toggleClass('oz-show-all-num', value);
|
||||
this.plugin.settings.showAllNumbers = value;
|
||||
this.plugin.saveSettings();
|
||||
}));
|
||||
new obsidian.Setting(containerEl)
|
||||
.setName('Add Root Folder')
|
||||
.setDesc('By default, there is no root folder provided by Obsidian. It is moved to drop-down menu to switch between vaults. ' +
|
||||
'Enable this option if you want to see root folder and its count in the file explorer')
|
||||
.addToggle((toggle) => toggle.setValue(this.plugin.settings.addRootFolder).onChange((value) => {
|
||||
this.plugin.settings.addRootFolder = value;
|
||||
this.plugin.saveSettings();
|
||||
this.plugin.reloadCount();
|
||||
}));
|
||||
this.filterOpt();
|
||||
}
|
||||
filterOpt() {
|
||||
new obsidian.Setting(this.containerEl)
|
||||
.setName('Show Only Markdown Notes')
|
||||
.setDesc('Turn off this option to choose file that should be counted')
|
||||
.addToggle((toggle) => toggle.setValue(this.showOnlyNoteValue).onChange((value) => {
|
||||
this.showOnlyNoteValue = value;
|
||||
this.plugin.reloadCount();
|
||||
this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
if (!this.showOnlyNoteValue) {
|
||||
new obsidian.Setting(this.containerEl)
|
||||
.setName('Filter List')
|
||||
.setDesc(createFragment((descEl) => {
|
||||
descEl.appendText('Extension list to include/exclude file during counting');
|
||||
descEl.appendChild(document.createElement('br'));
|
||||
descEl.appendText('Separated by comma');
|
||||
}))
|
||||
.addTextArea((text) => {
|
||||
const onChange = (value) => __awaiter(this, void 0, void 0, function* () {
|
||||
const list = value.split(',').map((v) => v.trim());
|
||||
this.plugin.settings.filterList = list;
|
||||
this.plugin.reloadCount();
|
||||
yield this.plugin.saveSettings();
|
||||
});
|
||||
text.setPlaceholder('Leave it empty to count all types of files');
|
||||
text.setValue(this.plugin.settings.filterList.join(', ')).onChange(obsidian.debounce(onChange, 500, true));
|
||||
text.inputEl.rows = 2;
|
||||
text.inputEl.cols = 25;
|
||||
});
|
||||
new obsidian.Setting(this.containerEl)
|
||||
.setName('Enable Blacklist')
|
||||
.setDesc('Turn on this option to use Filter List to exclude files')
|
||||
.addToggle((toggle) => toggle.setValue(this.plugin.settings.blacklist).onChange((value) => {
|
||||
this.plugin.settings.blacklist = value;
|
||||
this.plugin.reloadCount();
|
||||
this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FileExplorerNoteCount extends obsidian.Plugin {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.settings = DEFAULT_SETTINGS;
|
||||
this.vaultHandler = new VaultHandler(this);
|
||||
this.rootFolderEl = null;
|
||||
this.explorerNavHeaderSelector = '.workspace-leaf-content[data-type="file-explorer"] .nav-header';
|
||||
this.rootFolderClassName = 'oz-explorer-root-folder';
|
||||
this.initialize = (revert = false) => {
|
||||
let plugin = this;
|
||||
// First Check if the root folder exists
|
||||
let explorerHeaderEl = document.querySelector(`${this.explorerNavHeaderSelector} .${this.rootFolderClassName}`);
|
||||
if (explorerHeaderEl)
|
||||
this.rootFolderEl = explorerHeaderEl;
|
||||
const getViewHandler = (revert) => (view) => {
|
||||
this.fileExplorer = view;
|
||||
setupCount(this, revert);
|
||||
this.setupRootFolder(revert);
|
||||
if (!revert) {
|
||||
this.registerEvent(this.app.workspace.on('css-change', this.setupRootFolder));
|
||||
this.vaultHandler.registerVaultEvent();
|
||||
if (this.settings.showAllNumbers)
|
||||
document.body.addClass('oz-show-all-num');
|
||||
}
|
||||
else {
|
||||
for (const el of document.getElementsByClassName(withSubfolderClass)) {
|
||||
el.removeClass(withSubfolderClass);
|
||||
}
|
||||
document.body.removeClass(showAllNumbersClass);
|
||||
}
|
||||
if (!revert) {
|
||||
// when file explorer is closed (workspace changed)
|
||||
// try to update fehanlder with new file explorer instance
|
||||
this.register(around(view, {
|
||||
onClose: (next) => function () {
|
||||
setTimeout(() => doWithFileExplorer(plugin, getViewHandler(false)), 1e3);
|
||||
return next.apply(this);
|
||||
},
|
||||
}));
|
||||
}
|
||||
};
|
||||
doWithFileExplorer(plugin, getViewHandler(revert));
|
||||
};
|
||||
this.setupRootFolder = (revert = false) => {
|
||||
var _a, _b, _c;
|
||||
if (!this.fileExplorer) {
|
||||
console.error('file-explorer not found');
|
||||
return;
|
||||
}
|
||||
if (this.rootFolderEl && !this.settings.addRootFolder) {
|
||||
this.rootFolderEl.remove();
|
||||
this.rootFolderEl = null;
|
||||
}
|
||||
// Check if root is provided by Obsidian (it shouldn't be in the new releases)
|
||||
const root = (_c = (_b = (_a = this.fileExplorer) === null || _a === void 0 ? void 0 : _a.fileItems) === null || _b === void 0 ? void 0 : _b['/']) !== null && _c !== void 0 ? _c : null;
|
||||
if (!root) {
|
||||
// Get the Nav Header
|
||||
let explorerHeaderEl = document.querySelector(this.explorerNavHeaderSelector);
|
||||
if (!explorerHeaderEl)
|
||||
return;
|
||||
if (!this.rootFolderEl && this.settings.addRootFolder) {
|
||||
this.rootFolderEl = explorerHeaderEl.createEl('div', {
|
||||
cls: ['tree-item', 'nav-folder', this.rootFolderClassName],
|
||||
});
|
||||
this.rootFolderEl.innerHTML = `
|
||||
<div class="oz-explorer-root-nav-folder-title" data-path="/">
|
||||
<div class="tree-item-inner nav-folder-title-content">${this.app.vault.getName()}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
onload() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
console.log('loading FileExplorerNoteCount');
|
||||
this.addSettingTab(new FENoteCountSettingTab(this.app, this));
|
||||
yield this.loadSettings();
|
||||
this.app.workspace.onLayoutReady(this.initialize);
|
||||
});
|
||||
}
|
||||
onunload() {
|
||||
console.log('unloading FileExplorerNoteCount');
|
||||
this.initialize(true);
|
||||
}
|
||||
loadSettings() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
this.settings = Object.assign(Object.assign({}, this.settings), (yield this.loadData()));
|
||||
});
|
||||
}
|
||||
saveSettings() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield this.saveData(this.settings);
|
||||
});
|
||||
}
|
||||
reloadCount() {
|
||||
setupCount(this);
|
||||
}
|
||||
get fileFilter() {
|
||||
let list = this.settings.filterList;
|
||||
return (af) => {
|
||||
if (af instanceof obsidian.TFile) {
|
||||
const { extension: target } = af;
|
||||
// if list is empty, filter nothing
|
||||
if (list.length === 0)
|
||||
return true;
|
||||
else if (this.settings.blacklist)
|
||||
return !list.includes(target);
|
||||
else
|
||||
return list.includes(target);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FileExplorerNoteCount;
|
||||
|
||||
/* nosourcemap */
|
10
.obsidian/plugins/file-explorer-note-count/manifest.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "file-explorer-note-count",
|
||||
"name": "File Explorer Note Count",
|
||||
"version": "1.2.3",
|
||||
"minAppVersion": "1.2.0",
|
||||
"description": "The plugin helps you to see the number of notes under each folder within the file explorer.",
|
||||
"author": "Ozan Tellioglu",
|
||||
"authorUrl": "https://www.ozan.pl",
|
||||
"isDesktopOnly": false
|
||||
}
|
34
.obsidian/plugins/file-explorer-note-count/styles.css
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
.nav-folder-title[data-count]::after {
|
||||
content: attr(data-count);
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
font-size: calc(100% * 0.8);
|
||||
margin-right: 4px;
|
||||
/* border-radius: 3px; */
|
||||
padding: 2px 0;
|
||||
/* background-color: var(--background-secondary-alt); */
|
||||
transition: opacity 100ms ease-in-out;
|
||||
}
|
||||
|
||||
.oz-explorer-root-nav-folder-title {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.oz-explorer-root-nav-folder-title[data-count]::after {
|
||||
content: attr(data-count);
|
||||
margin-right: 4px;
|
||||
font-size: calc(100% * 0.8);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
body:not(.oz-show-all-num) .nav-folder:not(.is-collapsed) > .nav-folder-title.oz-with-subfolder[data-count]:not([data-path='/'])::after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nav-folder-title-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.oz-explorer-root-folder {
|
||||
margin-top: 15px;
|
||||
}
|
98
.obsidian/workspace.json
vendored
@ -4,21 +4,17 @@
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "edfb5e56b5f94247",
|
||||
"id": "4fa3bee0bb089abe",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "2c2a18b46ea36884",
|
||||
"id": "781acf562eb7f953",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "KMmpm/相关操作/容器目录管理/20.目录管理.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"type": "empty",
|
||||
"state": {},
|
||||
"icon": "lucide-file",
|
||||
"title": "20.目录管理"
|
||||
"title": "新标签页"
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -52,7 +48,7 @@
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "锁定",
|
||||
"query": "图片仅供参考",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
@ -92,11 +88,9 @@
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "KMmpm/相关操作/容器目录管理/20.目录管理.md"
|
||||
},
|
||||
"state": {},
|
||||
"icon": "lucide-list",
|
||||
"title": "20.目录管理 的大纲"
|
||||
"title": "大纲"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -118,7 +112,6 @@
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "SanPinPLM/相关操作/1.EDM/10.文件版本管理.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
@ -128,7 +121,7 @@
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-coming-in",
|
||||
"title": "10.文件版本管理 的反向链接列表"
|
||||
"title": "反向链接"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -162,44 +155,45 @@
|
||||
"hidden-folder-obsidian:显示文件夹": false
|
||||
}
|
||||
},
|
||||
"active": "262fdb4633fcf55d",
|
||||
"active": "781acf562eb7f953",
|
||||
"lastOpenFiles": [
|
||||
"KMmpm/相关操作/容器目录管理/20.目录管理.md",
|
||||
"未命名.canvas",
|
||||
"KMmpm/相关操作/容器目录管理/10.容器管理.md",
|
||||
"KMmpm/相关操作/账号启用.md",
|
||||
"KMmpm/相关操作/用户设置.md",
|
||||
"KMmpm/相关操作/数据导入.md",
|
||||
"KMmpm/相关操作/锁定与超时退出设置.md",
|
||||
"KMmpm/相关操作/密码更新周期.md",
|
||||
"KMmpm/相关操作/基于模板创建对象.md",
|
||||
"KMmpm/相关操作/开目Cloud启动.md",
|
||||
"KMmpm/相关操作/二开功能配置.md",
|
||||
"KMmpm/相关操作/容器目录管理/1.资料库.md",
|
||||
"SanPinPLM/相关操作/0.SETOUT/25.EDM实施必设参数.md",
|
||||
"SanPinPLM/报错处理/Office文件浏览时出现客户端闪退.md",
|
||||
"SanPinPLM/相关操作/4.other/90.重置密码.md",
|
||||
"SanPinPLM/相关操作/4.other/assets/Pasted image 20250210162109.png",
|
||||
"SanPinPLM/相关操作/0.SETOUT/42.角色设置.md",
|
||||
"SanPinPLM/相关操作/4.other/assets/Pasted image 20250210161934.png",
|
||||
"SanPinPLM/相关操作/0.SETOUT/40.用户设置.md",
|
||||
"SanPinPLM/相关操作/4.other/50.外部邮箱配置.md",
|
||||
"SanPinPLM/相关操作/4.other/75.系统日历设置.md",
|
||||
"SanPinPLM/相关操作/1.EDM/32.浩辰CAD图纸模板制作.md",
|
||||
"SanPinPLM/相关操作/1.EDM/31.浩辰CAD机械版图纸模板制作.md",
|
||||
"SanPinPLM/相关操作/1.EDM/28.AutoCAD图纸模板制作.md",
|
||||
"SanPinPLM/相关操作/1.EDM/65.文档模板.md",
|
||||
"SanPinPLM/相关操作/1.EDM/30.CAXA图纸模板制作.md",
|
||||
"SanPinPLM/相关操作/1.EDM/29.中望CAD图纸模板制作.md",
|
||||
"SanPinPLM/报错处理/填写CAD属性,中文显示成问号.md",
|
||||
"SanPinPLM/相关操作/2.PDM/11.中望CAD接口安装.md",
|
||||
"SanPinPLM/报错处理/文件没有成功生成.md",
|
||||
"SanPinPLM/报错处理/查看Office文件提示没有正确安装文档浏览器.md",
|
||||
"SanPinPLM/报错处理/后缀为.prt文件导入失败.md",
|
||||
"SanPinPLM/报错处理/导入BOM出现属性错误.md",
|
||||
"SanPinPLM/报错处理/打不开常规属性自定义界面.md",
|
||||
"SanPinPLM/相关操作/1.EDM/185.流程分支设置.md",
|
||||
"SanPinPLM/相关操作/1.EDM/75.文档审批流程模板制作.md",
|
||||
"SanPinPLM/相关操作/1.EDM/175.窗体表单.md",
|
||||
"SanPinPLM/相关操作/1.EDM/200.Delphi脚本.md",
|
||||
"SanPinPLM/相关操作/1.EDM/205.窗体表单查询.md",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212222546.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212222527.png",
|
||||
"SanPinPLM/相关操作/1.EDM/210.文档变更记录分类.md",
|
||||
"SanPinPLM/相关操作/1.EDM/195.窗体表单实例导出.md",
|
||||
"SanPinPLM/相关操作/1.EDM/190.窗体表单实例.md",
|
||||
"SanPinPLM/相关操作/1.EDM/215.文档变更管理.md",
|
||||
"SanPinPLM/相关操作/1.EDM/8.执行文档审批流程.md",
|
||||
"SanPinPLM/相关操作/1.EDM/70.电子签章设置.md",
|
||||
"KMmpm/系统介绍/安全管理/功能授权.md",
|
||||
"KMmpm/系统介绍/系统定制/对象模板.md",
|
||||
"KMmpm/相关操作/容器目录管理/assets/Pasted image 20250210154058.png",
|
||||
"KMmpm/系统介绍/系统定制/菜单定义.md",
|
||||
"KMmpm/系统介绍/数据建模/对象类定义/模板描述.md",
|
||||
"KMmpm/系统介绍/安全管理/assets/Pasted image 20250210153518.png",
|
||||
"KMmpm/相关操作/容器目录管理/assets/Pasted image 20250210153400.png",
|
||||
"KMmpm/相关操作/容器目录管理/assets/Pasted image 20250210152206.png",
|
||||
"KMmpm/相关操作/容器目录管理/assets/Pasted image 20250210152006.png",
|
||||
"KMmpm/相关操作/assets/Pasted image 20250210150426.png",
|
||||
"KMmpm/相关操作/assets/Pasted image 20250210150056.png",
|
||||
"KMmpm/系统介绍/系统定制/系统配置.md",
|
||||
"KMmpm/系统介绍/系统定制/assets/Pasted image 20250210143555.png",
|
||||
"外发客户教程/管理员操作教程/25.电子签章设置.md",
|
||||
"SanPinPLM/相关操作/1.EDM/77.文档审批流程.md",
|
||||
"SanPinPLM/相关操作/1.EDM/5.文件生命周期.md",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212092839.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212090345.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211164531.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211160555.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211154456.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211154256.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211154120.png",
|
||||
"SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211152526.png",
|
||||
"未命名.canvas",
|
||||
"KMmpm/系统介绍/数据建模/对象类定义/assets",
|
||||
"KMmpm/系统介绍/数据建模/对象类定义",
|
||||
"KMmpm/系统介绍/数据建模",
|
||||
|
BIN
.trash/Pasted image 20250211141703.png
Normal file
After Width: | Height: | Size: 361 KiB |
Before Width: | Height: | Size: 289 KiB After Width: | Height: | Size: 289 KiB |
@ -30,4 +30,8 @@
|
||||
|
||||
对文件点击浏览不再出现报错
|
||||
|
||||

|
||||

|
||||
|
||||
# 问题原因
|
||||
|
||||
这是因为没有为该客户端进行 [浏览器安装、调试](../相关操作/0.SETOUT/20.浏览器安装、调试.md)
|
@ -4,7 +4,7 @@
|
||||
|
||||

|
||||
|
||||
打开后选择 **服务** 菜单栏,在弹出的菜单列表中选择 “ 停止 ” 按钮
|
||||
打开后选择 **服务** 菜单栏,在弹出的菜单列表中选择 **停止** 按钮
|
||||
|
||||

|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
## 自动备份
|
||||
|
||||
配置三品PLM系统的自动备份工作,需要在备份页面点击 “ 设置 ” 按钮,会打开备份页面的设置窗口
|
||||
配置三品PLM系统的自动备份工作,需要在备份页面点击 **设置** 按钮,会打开备份页面的设置窗口
|
||||
|
||||
在此窗口,勾选自动备份选项能够开启自动备份工作,根据需求配置按星期进行备份或按月份进行备份
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
|
||||
还原程序与手动备份程序一样,当三品服务端的服务停止后,在三品PLM服务端的安装目录下找到 Server 文件夹,在该文件夹中找到 `cfManualAction` 应用程序,如果显示了后缀名的话是 `cfManualAction.exe`
|
||||
|
||||
双击运行该文件后,打开备份与还原工具,点击 “ 还原按钮 ”
|
||||
双击运行该文件后,打开备份与还原工具,点击 **还原按钮**
|
||||
|
||||

|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
# 前置要求
|
||||
|
||||
服务器 [IP地址固定](11.IP地址固定.md) 且 [服务端安装、端口设置](12.服务端安装、端口设置.md) 完成
|
||||
|
||||
# 操作
|
||||
|
||||
先关闭电脑上的杀毒软件,然后双击打开服务端安装程序
|
||||
|
@ -1,3 +1,9 @@
|
||||
# 前置要求
|
||||
|
||||
完成了 [数据库安装](10.数据库安装.md) 、 [IP地址固定](11.IP地址固定.md) 、 [服务端安装、端口设置](12.服务端安装、端口设置.md) 工作
|
||||
|
||||
# 操作
|
||||
|
||||
双击运行 **使用情况统计** 安装程序
|
||||
|
||||

|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
### 相关参数
|
||||
|
||||
在默认情况下,**文档变更** 流程中,只有 **变更操作** 这一过程类型,是没有 **变更生效** 的,需要开启相关的参数才能使用 **变更生效** 过程类型
|
||||
在默认情况下,**文档变更** 流程中,只有 **变更操作** 这一过程类型,是没有 [变更生效](215.文档变更管理.md#变更生效) 的,需要开启相关的参数才能使用 **变更生效** 过程类型
|
||||
|
||||
### 变更操作
|
||||
|
||||
@ -35,20 +35,10 @@
|
||||
|
||||
### 变更生效
|
||||
|
||||
普通的审批流程中文件是需要进行归档的,但变更流程中文件本来就是归档状态的,在整个变更过程中文件都将保持归档状态,故而是不需要 **归档** 过程的。
|
||||
普通的审批流程中文件是需要进行归档的,但变更流程中文件本来就是归档状态的,在整个变更过程中文件都将保持归档状态,故而是不需要 **归档** 过程的
|
||||
|
||||
同时若开启了 [变更生效](210.文件变更管理.md#变更生效) 过程类型,就需要在流程中设置此过程,让对文档的变更操作生效
|
||||
若没有开启 [变更生效](215.文档变更管理.md#变更生效) 过程类型,那么在执行了 **变更操作** 过程后,直接就完成了对文档的变更(直接改动,略过了对变更后的文件的校验工作)
|
||||
|
||||

|
||||
同时若开启了 [变更生效](215.文档变更管理.md#变更生效) 过程类型,就需要在流程中设置此过程,让对文档的变更操作生效(相当于 [归档](5.文件生命周期.md#归档) 操作),若没有设置此过程,则变更无效,即便走完了此流程,归档区中也没有变更后的文件
|
||||
|
||||
#### 前置要求
|
||||
|
||||
进行参数设置的用户角色必须具备 “ 系统设置 ” 中的 “ 参数配置 ” 权限
|
||||
|
||||

|
||||
|
||||
#### 开启参数
|
||||
|
||||
**系统设置** → **参数配置** → 搜索 → **变更生效** → **==启用“变更生效”类型的流程过程==**
|
||||
|
||||

|
||||

|
0
SanPinPLM/相关操作/1.EDM/104.文档变更流程.md
Normal file
@ -70,7 +70,7 @@
|
||||
|
||||
明细表属性来源于窗体表单的 [明细表配置](#明细表配置) ,分单行明细表和多行明细表
|
||||
|
||||
单行明细表在属性界面上由 **明细单行** 容器控件 及在容器控件中的标签控件、编辑框控件及按钮控件组成
|
||||
单行明细表在属性界面上由 **明细单行** 容器控件及在容器控件中的标签控件、编辑框控件及按钮控件组成
|
||||
|
||||
多行明细表在属性界面上由网格控件来显示及操作数据
|
||||
|
||||
@ -147,7 +147,7 @@
|
||||
- **按钮类型**:编辑框控件设置按钮的类型,可设置值:正常、选择、下拉、只读选择
|
||||
- **数据类型**:控件设置取值类型,可设置值:文本、整数、小数、日期、日期时间、字母
|
||||
- **使用初始值**:编辑框控件设置是否使用初始值
|
||||
- **初始值类型**:编辑框控件设置初始值类型,“使用初始值”启用时属性设置才有效,可设置值:用户输入、当前用户 ID、当前用户岗位、当前用户部门、当前日期时间、当前日期、当前 时间、当前年、当前月、当前日、编码、创建时间、修改时间
|
||||
- **初始值类型**:编辑框控件设置初始值类型,“使用初始值”启用时属性设置才有效,可设置值:用户输入、当前用户 ID、当前用户岗位、当前用户部门、当前日期时间、当前日期、当前时间、当前年、当前月、当前日、编码、创建时间、修改时间
|
||||
- **初始值文本**:编辑框控件设置输入文本类型初始值,“使用初始值”启用且“初始值类 型”为“用户输入”时属性设置才有效
|
||||
- **字体颜色**:设置字体大小及样式颜色
|
||||
- **脚本**:编写脚本内容
|
||||
@ -156,4 +156,4 @@
|
||||
- **单击执行**:设置 [窗体表单脚本](200.Delphi脚本.md) ,窗体表单实例或编辑对象常规属性时,脚本在单击控件时执行
|
||||
- **双击执行**:设置 [窗体表单脚本](200.Delphi脚本.md) ,窗体表单实例或编辑对象常规属性时,脚本在双击控件时执行
|
||||
- **获得焦点执行**:设置 [窗体表单脚本](200.Delphi脚本.md) ,窗体表单实例或编辑对象常规属性时,脚本在控件获取焦点时执行
|
||||
- **失去焦点执行**:设置脚 [窗体表单脚本](200.Delphi脚本.md) ,窗体表单实例或编辑对象常规属性时,脚本在控件失去焦点时执行
|
||||
- **失去焦点执行**:设置 [窗体表单脚本](200.Delphi脚本.md) ,窗体表单实例或编辑对象常规属性时,脚本在控件失去焦点时执行
|
@ -4,7 +4,7 @@
|
||||
|
||||
# 窗体表单模板制作
|
||||
|
||||
## 前提要求
|
||||
## 前置要求
|
||||
|
||||
创建窗体表单模板的用户必须具备 **企业配置** 中的 **对象分类** 权限
|
||||
|
||||
|
@ -1,20 +1,26 @@
|
||||
# 介绍
|
||||
|
||||
窗体表单实例中,除创建者以外,其他人对该窗体表单都是只读的
|
||||
窗体表单实例中,除创建者以外,其他人对该窗体表单都是只读的,包括在走流程时,即便过程用户具备相关权限,也无法对窗体表单实例进行修改
|
||||
|
||||
只有在窗体表单模板中对属性编辑框或网格列绑定流程过程用户后,在特定的时间段(用户在流程相应的流程过程中)才能编辑相关属性,用户想要在一开时能够编辑窗体表单的属性,则需要在窗体表单模板中为该属性绑定流程开始过程中的流程启动者
|
||||
只有在窗体表单模板中对属性编辑框或网格列绑定流程过程用户后,在特定的时间段(用户在流程相应的流程过程中)才能编辑相关属性,用户想要在一开始能够编辑窗体表单的属性,则需要在窗体表单模板中为该属性绑定流程开始过程中的流程启动者
|
||||
|
||||
## 前置要求
|
||||
|
||||
要为 [窗体表单](175.窗体表单.md) 绑定流程过程用户,需要先为 **窗体表单** 绑定 **流程模板**
|
||||
|
||||

|
||||
|
||||
## 编辑控件绑定流程过程用户
|
||||
|
||||
在 “ [属性界面区](175.窗体表单.md#属性界面区) ” 单击选中 “ 编辑框 ” 控件,单击 “ 关联流程过程 ” 按钮
|
||||
在 [属性界面区](175.窗体表单.md#属性界面区) 单击选中 **编辑框** 控件,单击 **添加流程过程用户** 按钮
|
||||
|
||||

|
||||
|
||||
在 “ 设置关联流程过程 ” 窗体中,单击 “ 添加 ” 按钮
|
||||
在 **设置关联流程过程** 窗体中,单击 **添加** 按钮
|
||||
|
||||

|
||||
|
||||
在 “ 选择流程步骤 ” 窗体,单击选择 “ 流程模板 ” 列表中的流程模板,单击选择 “ 流程图 ” 中的流程过程,单击选择 “ 过程参与人 ”,单击 “ 确定 ” 按钮确定选择
|
||||
在 **选择流程步骤** 窗体,单击选择 **流程模板** 列表中的流程模板,单击选择 **流程图** 中的流程过程,单击选择 **过程参与人**,单击 **确定** 按钮确定选择
|
||||
|
||||

|
||||
|
||||
@ -22,15 +28,15 @@
|
||||
|
||||
## 网格控件绑定流程过程用户
|
||||
|
||||
在 “ [属性界面区](175.窗体表单.md#属性界面区) ” 单击选中 “ 网格 ” 控件,单击 “ 关联流程过程 ” 按钮
|
||||
在 [属性界面区](175.窗体表单.md#属性界面区) 单击选中 **网格** 控件,单击 **关联流程过程** 按钮
|
||||
|
||||

|
||||
|
||||
在 “ 设置流程过程关联 ” 窗体,在 “ 列信息 ” 下单击选择行,单击 “ 增加 ” 按钮
|
||||
在 **设置流程过程关联** 窗体,在 **列信息** 下单击选择行,单击 **增加** 按钮
|
||||
|
||||

|
||||
|
||||
在 “ 选择流程步骤 ” 窗体,单击选择 “ 流程模板 ” 列表中的流程模板,单击选择 “ 流程图 ” 中的流程过程,单击选择 “ 过程参与人 ”,单击 “ 确定 ” 按钮确定选择
|
||||
在 **选择流程步骤** 窗体,单击选择 **流程模板** 列表中的流程模板,单击选择 **流程图** 中的流程过程,单击选择 **过程参与人**,单击 **确定** 按钮确定选择
|
||||
|
||||

|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
# 操作步骤
|
||||
|
||||
首先选择 “ 企业配置 ” ,在 ” 对象分类 “ 中选择 ” 窗体表单 “ ,以 ” 更改申请/审批表 ” 为例,这个窗体表单需要多个部门进行会签,且根据需求的不同,每次会签涉及到的部门都不同
|
||||
首先选择 **企业配置**,在 **对象分类** 中选择 **窗体表单** ,以 “更改申请/审批表” 为例,这个窗体表单需要多个部门进行会签,且根据需求的不同,每次会签涉及到的部门都不同
|
||||
|
||||

|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
以该窗体表单为例,想要为其设置流程分支,**需要先对窗体表单进行准备设置**
|
||||
|
||||
在 “ 窗体表单配置 ” 中,已经有了相应部门的字段
|
||||
在 **窗体表单配置** 中,已经有了相应部门的字段
|
||||
|
||||

|
||||
|
||||
@ -26,13 +26,13 @@
|
||||
|
||||

|
||||
|
||||
新增后点击应用,就能在 “ 常规属性 ” 中直接进行配置,通过控件区将新增的字段添加到 “ 常规属性 ” 中
|
||||
新增后点击应用,就能在 **常规属性** 中直接进行配置,通过控件区将新增的字段添加到 **常规属性** 中
|
||||
|
||||
条件字段是布尔类型,所以添加时选择标签类型为 “ 是否 ” 即可
|
||||
条件字段是布尔类型,所以添加时选择标签类型为 **是否** 即可
|
||||
|
||||

|
||||
|
||||
添加完成后选择所有字段,通过右侧的 [控件对齐区](175.窗体表单.md#控件对齐区) 将其排列整齐后点击确定即可
|
||||
添加完成后选择所有字段,通过右侧的 [控件对齐区](170.自定义控件页面.md#控件对齐区) 将其排列整齐后点击确定即可
|
||||
|
||||

|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||

|
||||
|
||||
对前一个过程节点的执行策略设置为:“ 有选择转移 ” → “ 条件选择 ” → “ 自动条件选择 ”,这样设置的话,PLM系统会根据条件情况去自动进行匹配
|
||||
对前一个过程节点的执行策略设置为: [提交策略](75.文档审批流程模板制作.md#提交策略) → [条件选择](75.文档审批流程模板制作.md#条件选择) → **自动条件选择**,这样设置的话,PLM系统会根据条件情况去自动进行匹配
|
||||
|
||||

|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
可以不为该分支命名,直接设置条件
|
||||
|
||||
选择 “ 依据窗体表单 ”,并选择我们要绑定的窗体表单,点击增加后选择对应的条件字段,并设置满足条件的值(条件字段是布尔字段,所以值只有是与否)
|
||||
选择 **依据窗体表单**,并选择我们要绑定的 [窗体表单](175.窗体表单.md) ,点击增加后选择对应的条件字段,并设置满足条件的值(条件字段是布尔字段,所以值只有是与否)
|
||||
|
||||
其他设置与流程模板设置相同,完成后点击确定即可
|
||||
|
||||
@ -66,6 +66,6 @@
|
||||
|
||||
# 测试
|
||||
|
||||
创建窗体表单,并选择某一部门跑流程测试,会发现在只勾选了车用部的情况下,流程自动提交给了车用部,而其他分支没有执行
|
||||
创建窗体表单,并选择某一部门跑流程测试,会发现在只勾选了车用部的情况下,流程自动提交给了车用部,并没有流转到其他分支
|
||||
|
||||

|
@ -2,23 +2,25 @@
|
||||
|
||||
窗体表单实例的创建方式:通过 [模块菜单组](#在模块菜单下创建窗体表单)、在 [文档工作区](#在文档工作区创建窗体表单实例)、[属性窗体的对象附件](#在属性窗体对象附件页下创建窗体表单实例) 中创建窗体表单实例
|
||||
|
||||
说明:窗体表单实例中,除创建者以外,其他人对该窗体表单都是只读的,包括在走流程时,即便过程用户具备相关权限,也无法对窗体表单实例进行修改,想要修改,需要为 [窗体表单绑定流程过程用户](180.窗体表单绑定流程过程用户.md)
|
||||
|
||||
# 在文档工作区创建窗体表单实例
|
||||
|
||||
## 前置条件
|
||||
|
||||
最基础的前置条件为 [窗体表单设置的前提要求](175.窗体表单.md#前提要求)
|
||||
最基础的前置条件为 [窗体表单设置的前提要求](175.窗体表单.md#前置要求)
|
||||
|
||||
选择 “ 企业配置 ” 模块,进入 “ 对象分类 ” 选项卡,在分类树结构的 “ 窗体表单 ” 节点下选中窗体表单模板分类节点,右键,选择 “ 属性 ” ,打开分类窗体表单,勾选 “ 在“新建”菜单中显示 ” 选项
|
||||
选择 **企业配置** 模块,进入 **对象分类** 选项卡,在分类树结构的 **窗体表单** 节点下选中窗体表单模板分类节点,右键,选择 **属性**,打开分类窗体表单,勾选 **在“新建”菜单中显示** 选项
|
||||
|
||||

|
||||
|
||||
用户要在文档工作区中创建窗体表单,还需要在 “ 系统设置 ” 模块中选择 “ 参数配置 ” 选项卡,在参数配置结构树中选择 “ 权限相关 ” 选项,选择其中的 “ 启用窗体表单功能 ” 参数,对其设置勾选(启用)
|
||||
用户要在文档工作区中创建窗体表单,还需要在 **系统设置** 模块中选择 **参数配置** 选项卡,在参数配置结构树中选择 **权限相关** 选项,选择其中的 **启用窗体表单功能** 参数,对其设置勾选(启用)
|
||||
|
||||

|
||||
|
||||
## 操作
|
||||
|
||||
前置条件得到满足后,用户在文档工作区右键,选择 “ 新建 ”,在子菜单中选择 “ 窗体表单 ” 并选择到对应的分类即可完成创建
|
||||
前置条件得到满足后,用户在文档工作区右键,选择 **新建**,在子菜单中选择 **窗体表单** 并选择到对应的分类即可完成创建
|
||||
|
||||

|
||||
|
||||
@ -92,7 +94,7 @@
|
||||
|
||||
## 前置要求
|
||||
|
||||
最基础的前置条件为[窗体表单设置的前提要求](175.窗体表单.md#前提要求)
|
||||
最基础的前置条件为[窗体表单设置的前提要求](175.窗体表单.md#前置要求)
|
||||
|
||||
### 新增变更记录
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||

|
||||
|
||||
需要在Excel表中对应的输入位置上,对名称栏设置相应的属性名(属性名需要与系统中的属性名对应,可在自定义布局界面右键属性,选择 “ 复制名称 ” 取得)
|
||||
需要在Excel表中对应的输入位置上,对名称栏设置相应的属性名(属性名需要与系统中的属性名对应,可在自定义布局界面右键属性,选择 **复制名称** 取得)
|
||||
|
||||
明细表字段,在明细表配置界面进行上述操作相同过程进行复制,序列号字段可直接点击 “ 复制序号列名称 ”
|
||||
|
||||
|
@ -140,7 +140,7 @@
|
||||
|
||||
## 可见
|
||||
|
||||
赋予可见权限后,用户能够看见该文件或文件夹,但是不能进行任何操作,也无法对文件进行任何操作,包括浏览
|
||||
赋予可见权限后,用户能够看见该文件或文件夹,但是不能进行任何操作,也无法对文件进行任何操作,包括浏览(请不要为 [角色](../0.SETOUT/42.角色设置.md) 赋予文档发布区的 **可见** 权限,否则 [文档发布](15.文件的收发管理.md#文档发布) 后,即便该角色的用户没有收到文件,也能够在文档发布区查看到该文件 )
|
||||
|
||||
|  |  |
|
||||
| -------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
|
@ -10,12 +10,12 @@
|
||||
|
||||
# 窗体表单查询定义
|
||||
|
||||
根据窗体表单分类来定义查询界面
|
||||
根据窗体表单分类来定义查询界面,每个用户各自定义的查询方案是不通用的
|
||||
|
||||
## 操作
|
||||
|
||||
1. 单击 **新建** 按钮,进入 **窗体表单查询定义** 窗体
|
||||
2. 单击 **表单分类** 的选择按钮,选择窗体表单分类,为该分类设置查询界面
|
||||
2. 单击 **表单分类** 的选择按钮,选择 [窗体表单](175.窗体表单.md) 分类,为该分类设置查询界面
|
||||
3. 在 **查询条件区** 右键,选择 **自定义界面** ,进入自定义界面设置查询界面
|
||||
4. 在 **查询结果区**,右键网格列标题,选择 **自定义列**,进入显示列定义界面
|
||||
5. 定义好界面后,单击 **确定** 按钮保存数据
|
||||
@ -24,9 +24,9 @@
|
||||
|
||||
# 窗体表单我的查询
|
||||
|
||||
当前登录用户通过自己新建的查询界面,查询当前登录用户所创建的窗体表单实例
|
||||
当前登录用户通过自己新建的查询界面,查询当前登录用户所创建的 [窗体表单实例](190.窗体表单实例.md) ,方案绑定的是哪一类 [窗体表单](175.窗体表单.md) ,就只能查询到这一类窗体表单
|
||||
|
||||

|
||||

|
||||
|
||||
- **方案名称**:选择查询定义的名称,选择后,查询条件界面自动加载查询定义中设置的界面
|
||||
- **选择文件夹**:默认查询条件,选择搜索文件夹
|
||||
@ -36,4 +36,6 @@
|
||||
|
||||
# 窗体表单查询管理
|
||||
|
||||
与 [窗体表单我的查询](#窗体表单我的查询) 操作雷同,此处不作介绍
|
||||
与 [窗体表单我的查询](#窗体表单我的查询) 操作雷同,但查询范围是所有用户,而非当前登录用户,此处不作介绍
|
||||
|
||||

|
@ -1,37 +0,0 @@
|
||||
# 变更管理
|
||||
|
||||
|
||||
|
||||
# 扩展
|
||||
|
||||
## 相关参数
|
||||
|
||||
### 前置要求
|
||||
|
||||
进行参数设置的用户角色必须具备 **系统设置** 中的 **参数配置** 权限
|
||||
|
||||

|
||||
|
||||
### 变更生效
|
||||
|
||||
普通的审批流程中文件是需要进行归档的,但变更流程中文件本来就是归档状态的,在整个变更过程中文件都将保持归档状态,故而是不需要 [归档](5.文件生命周期.md#归档) 过程的
|
||||
|
||||
若没有开启 **变更生效** 过程类型,那么在执行了 [变更操作](102.文档变更流程模板制作.md#变更操作) 过程后,直接就完成了对文档的变更(直接改动,略过了对变更后的文件的校验工作)
|
||||
|
||||
开启 **变更生效** 过程类型,那么需要在流程中设置此过程,让对文档的变更操作生效(相当于 [归档](5.文件生命周期.md#归档) 操作),若没有设置此过程,则变更无效,即便走完了此流程,归档区中也没有变更后的文件
|
||||
|
||||

|
||||
|
||||
#### 开启参数
|
||||
|
||||
**系统设置** → **参数配置** → 搜索 → **变更生效** → **==启用“变更生效”类型的流程过程==**
|
||||
|
||||

|
||||
|
||||
### 发布态文件变更
|
||||
|
||||
处于 [发布态](5.文件生命周期.md#发布态) 的文件,若要进行文件 [变更](5.文件生命周期.md#变更),通常是需要先进行 [回收](5.文件生命周期.md#回收),然后再进行变更
|
||||
|
||||
通过开启参数 **发布态的文件可以变更**,可使发布态的文件无需回收,直接进入进行变更
|
||||
|
||||

|
95
SanPinPLM/相关操作/1.EDM/210.文档变更记录分类.md
Normal file
@ -0,0 +1,95 @@
|
||||
# 介绍
|
||||
|
||||
[文档变更流程](104.文档变更流程.md) 与 [文档审批流程](8.执行文档审批流程.md) 的不同之处不仅在于流程类型的不同,还在于 **文档变更流程** 默认关联了 **普通变更记录**
|
||||
|
||||

|
||||
|
||||
故而在文档执行完 [文档变更流程](104.文档变更流程.md) 后,通过该文档的 **变更记录** 页签都可查询到该文档的所有 **变更记录**
|
||||
|
||||

|
||||
|
||||
为了将变更的相关信息统一记录在文档的变更记录上,可根据对应的 [文档分类设置](80.文档分类设置.md) 来设置对应的 [文档变更记录分类](../../../..//SanPinPLM/相关操作/1.EDM/210.文档变更记录分类.md) ,这样在发起 [文档变更流程](104.文档变更流程.md) 后可在变更记录中记录更多的信息,包括但不限于常规信息、申请单、通知单等
|
||||
|
||||

|
||||
|
||||
## 前置要求
|
||||
|
||||
对 **对象分类** 进行操作,需要用户角色至少具备 **对象分类** 的 **可见、浏览、修改、新增** 权限
|
||||
|
||||

|
||||
|
||||
## 创建文档变更记录
|
||||
|
||||
在 **企业配置** → **对象分类** → **变更记录** 中可以创建 **变更记录**,对 “变更记录” 或变更记录分类右键,选择 **分类** 即可新建一个变更记录分类
|
||||
|
||||

|
||||
|
||||
### 名称
|
||||
|
||||
设置的是 **变更记录分类** 的名称,该名称即在系统中对文档发起 **变更流程** 时,变更记录的所属分类
|
||||
|
||||

|
||||
|
||||
### 标签名称
|
||||
|
||||
系统默认创建的 **普通变更记录** 分类,在没有设置 **标签名称** 的情况下,其 **变更记录** 可对 **物料**、**文档**、**项目**、**工艺路线** 等信息进行管理
|
||||
|
||||

|
||||
|
||||
对于特定的 **变更记录分类**,要管理的信息不需要这么多,那么就可以通过 **标签名称** 来进行限制。本篇笔记以 **文档变更记录分类** 为主,故而==设置 **标签名称** 为 **文档**== 后,在 **变更记录** 中就不会存在物料、项目、工艺路线等页签了
|
||||
|
||||

|
||||
|
||||
### 流程模板
|
||||
|
||||
**变更记录分类** 需要绑定的是 **流程模板**,而不像 [文档分类](80.文档分类设置.md) 那样需要绑定 [创建工作流](80.文档分类设置.md#创建工作流) ,这样在 **变更管理** → **变更申请** 中创建出新的变更记录可以直接 **创建工作流**
|
||||
|
||||
注意:需要为 **变更记录分类** 绑定 [文档变更流程模板](102.文档变更流程模板制作.md)
|
||||
|
||||

|
||||
|
||||
#### 前置要求
|
||||
|
||||
**变更记录分类** 需要绑定的是 **文档变更流程模板**,先在 **流程模板** 功能区中制作好,文档的变更流程制作教程见:[文档变更流程模板](102.文档变更流程模板制作.md)
|
||||
|
||||

|
||||
|
||||
### 编码器
|
||||
|
||||
配置 **变更记录分类** 的编码器需要先勾选 **编码器**,然后通过选择框去 **编码管理** 功能区选择要为该变更记录分类绑定的编码器,这样在创建出该变更记录后,可以通过编码器自动生成出相应的编码
|
||||
|
||||
变更记录分类配置编码器操作,与 [物料分类配置编码器](../2.PDM/25.物料分类设置.md#物料编码器) 操作相同(图片仅供参考,实际效果一致)
|
||||
|
||||

|
||||
|
||||
### 常规属性设置
|
||||
|
||||
**常规属性** 指设置该对象类型的文档,其 **常规** 页签中的属性,任何变更记录分类可以 [自定义对象常规属性](../4.other/20.自定义对象常规属性.md)
|
||||
|
||||

|
||||
|
||||
## 对象窗体表单
|
||||
|
||||
每个 **变更记录分类** 都可以绑定两个 [窗体表单](175.窗体表单.md) ,并设置其类型为 **通知单** 或 **申请单**(可以绑定多个窗体表单,但每个窗体表单都要设置其类型,且只有最后绑定的窗体表单才会生效)
|
||||
|
||||

|
||||
|
||||
设置 **窗体表单** 类型后,勾选 **默认创建**,可使得在创建出该变更记录后,默认自带该页签
|
||||
|
||||

|
||||
|
||||
### 前置要求
|
||||
|
||||
**窗体表单** 需要先在 **企业配置** → **对象分类** 中制作好,制作流程见: [窗体表单](175.窗体表单.md)
|
||||
|
||||
且为了在走流程时,用户能够在窗体表单中填写信息,需要完成 [窗体表单绑定流程过程用户](180.窗体表单绑定流程过程用户.md) 操作
|
||||
|
||||

|
||||
|
||||
## 流程模板绑定变更记录
|
||||
|
||||
[文档变更流程模板](102.文档变更流程模板制作.md) 可以与 [文档变更记录分类](../../../..//SanPinPLM/相关操作/1.EDM/210.文档变更记录分类.md) 进行绑定,从而实现对文档执行变更流程时,自动创建相应的 **变更记录** 出来
|
||||
|
||||
在流程模板属性定义界面中的 **流程设置** 页签中的 **变更记录** 一栏进行绑定
|
||||
|
||||

|
61
SanPinPLM/相关操作/1.EDM/215.文档变更管理.md
Normal file
@ -0,0 +1,61 @@
|
||||
# 变更管理
|
||||
|
||||
文档的 **变更管理** 是 [文档变更流程模板](102.文档变更流程模板制作.md) 的进阶,对于三品PLM系统而言,变更管理不局限于文档,还包含了 **物料**、**项目**、**变更任务**、**工艺路线** 的变更管理,本篇笔记以文档变更管理为主展开介绍
|
||||
|
||||
**文档变更记录** 指的是 [标签名称](210.文档变更记录分类.md#标签名称) 设置为 **文档** 的 **变更记录分类**,通过 **变更管理** 能够对系统所有的 **变更记录** 进行管理
|
||||
|
||||
## 变更申请
|
||||
|
||||
变更申请中,记录所有由当前用户发起的变更,无论是对文档发起 [文档变更流程](104.文档变更流程.md) 还是直接在 **变更申请** 中发起,都会被记录到 **变更申请** 中
|
||||
|
||||

|
||||
|
||||
### 变更申请与文档变更的不同
|
||||
|
||||
对于系统而言,文档变更就是发起了变更申请,其变更流程是一致的,仅在操作上,略有差别
|
||||
|
||||
[文档变更流程](104.文档变更流程.md) 是通过文档 [生命周期](5.文件生命周期.md#生命周期) 的 [变更](5.文件生命周期.md#变更) 去走 **变更流程**,这样操作的话,变更记录中默认添加了要变更的文件,用户在发起变更的 [开始过程](75.文档审批流程模板制作.md#开始过程与结束过程) 不能进行操作,需要在开始过程后的 [变更操作](102.文档变更流程模板制作.md#变更操作) 过程去对文件进行变更
|
||||
|
||||
而通过 **变更申请** 去发起新的 **变更记录**,用户能够在发起流程之前,在 **变更记录** 中填写 **申请单**、**通知单** 以及添加要变更的文档,直接进行变更,相当于代替了 [变更操作](102.文档变更流程模板制作.md#变更操作) 过程
|
||||
|
||||

|
||||
|
||||
# 扩展
|
||||
|
||||
## 相关参数
|
||||
|
||||
### 前置要求
|
||||
|
||||
进行参数设置的用户角色必须具备 **系统设置** 中的 **参数配置** 权限
|
||||
|
||||

|
||||
|
||||
### 变更生效
|
||||
|
||||
普通的审批流程中文件是需要进行归档的,但变更流程中文件本来就是归档状态的,在整个变更过程中文件都将保持归档状态,故而是不需要 [归档](5.文件生命周期.md#归档) 过程的
|
||||
|
||||
若没有开启 **变更生效** 过程类型,那么在执行了 [变更操作](102.文档变更流程模板制作.md#变更操作) 过程后,直接就完成了对文档的变更(直接改动,略过了对变更后的文件的校验工作)
|
||||
|
||||
开启 **变更生效** 过程类型,那么需要在流程中设置此过程,让对文档的变更操作生效(相当于 [归档](5.文件生命周期.md#归档) 操作),若没有设置此过程,则变更无效,即便走完了此流程,归档区中也没有变更后的文件
|
||||
|
||||

|
||||
|
||||
#### 开启参数
|
||||
|
||||
**系统设置** → **参数配置** → 搜索 → **变更生效** → **==启用“变更生效”类型的流程过程==**
|
||||
|
||||

|
||||
|
||||
##### 前置要求
|
||||
|
||||
进行参数设置的用户角色必须具备 **系统设置** 中的 **参数配置** 权限
|
||||
|
||||

|
||||
|
||||
### 发布态文件变更
|
||||
|
||||
处于 [发布态](5.文件生命周期.md#发布态) 的文件,若要进行文件 [变更](5.文件生命周期.md#变更),通常是需要先进行 [回收](5.文件生命周期.md#回收),然后再进行变更
|
||||
|
||||
通过开启参数 **发布态的文件可以变更**,可使发布态的文件无需回收,直接进入进行变更
|
||||
|
||||

|
@ -11,7 +11,7 @@
|
||||
|
||||
# 前置要求
|
||||
|
||||
对于要签字的文档,若希望签上电子签名,则需要确保PLM开启了[采用印章对Excel和Word文件进行电子签名](../0.SETOUT/25.EDM实施必设参数.md#采用印章对Excel和Word文件进行电子签名)参数。此外,还需要为[PLM设置签名属性采用的定位方式](../0.SETOUT/25.EDM实施必设参数.md#Excel文件签名属性采用的定位方式),这是为了PLM系统能够根据模板中的属性快速定位到对应位置进行签名,定位方式一旦确定,那么系统中所有该类型的文档都会采取相同的定位方式
|
||||
对于要签字的文档,若希望签上电子签名,则需要确保PLM开启了 [采用印章对Excel和Word文件进行电子签名](../0.SETOUT/25.EDM实施必设参数.md#采用印章对Excel和Word文件进行电子签名) 参数。此外,还需要为 [PLM设置签名属性采用的定位方式](../0.SETOUT/25.EDM实施必设参数.md#Excel文件签名属性采用的定位方式) ,这是为了PLM系统能够根据模板中的属性快速定位到对应位置进行签名,定位方式一旦确定,那么系统中所有该类型的文档都会采取相同的定位方式
|
||||
|
||||
制作文档模板,需要用户角色至少具备 **文档模板** 的 **可见、浏览、修改、新增** 权限
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
下面将围绕这三种定位方式来进行文档模板制作,并分析三种定位方式的优劣
|
||||
|
||||
Word文件请见[Word文档模板制作](26.Word文档模板制作.md)
|
||||
Word文件请见 [Word文档模板制作](26.Word文档模板制作.md)
|
||||
|
||||
## 文本框(最佳方式)
|
||||
|
||||
@ -82,7 +82,7 @@ Word文件请见[Word文档模板制作](26.Word文档模板制作.md)
|
||||
|
||||
4. 同一工作簿中有多个”同名“文本框,无论是否位于同一工作表,系统都可统一对这些”同名“文本框进行签字
|
||||
|
||||
5. 流程模板中, “ 属性名称 ” 设置为文本框“名称”即可,无需因多个工作表中存在”同名“文本框而进行特别设置
|
||||
5. 流程模板中,**属性名称** 设置为文本框“名称”即可,无需因多个工作表中存在”同名“文本框而进行特别设置
|
||||
|
||||
6. 不同工作表的”同名“文本框可处于不同位置(不同的单元格坐标)
|
||||
|
||||
@ -97,7 +97,7 @@ Word文件请见[Word文档模板制作](26.Word文档模板制作.md)
|
||||
|
||||
### 制作方式
|
||||
|
||||
选中指定单元格后,左上角原来显示C2(也就是单元格坐标)的位置,就是 “ 名称栏 ”,点击该位置写入新名称后回车即可
|
||||
选中指定单元格后,左上角原来显示C2(也就是单元格坐标)的位置,就是 **名称栏**,点击该位置写入新名称后回车即可
|
||||
|
||||
> **注意**:在名称框中输入新名称后一定是**回车**!这样才能生效
|
||||
>
|
||||
|
@ -243,7 +243,7 @@ SPCADPARAM参数块
|
||||
|
||||
# 补充
|
||||
|
||||
如果填写属性后,发现中文显示成问号,那么是因为当前字体中不包含中文字符,可以使用 `ST` 命令,在 **文字样式** 中调整文字样式使用的字体
|
||||
可能会出现 [填写CAD属性,中文显示成问号](../../报错处理/填写CAD属性,中文显示成问号.md) 的问题 ,那么是因为当前字体中不包含中文字符,可以使用 `ST` 命令,在 **文字样式** 中调整文字样式使用的字体
|
||||
|
||||

|
||||
|
||||
|
@ -243,7 +243,7 @@ C盘 → 用户 → 公共文档 → ZWSoft → ZWCADM → 里面有中望CAD安
|
||||
|
||||
## 前置要求
|
||||
|
||||
完成自定义标题栏、明细栏制作,并与中望CAD绑定
|
||||
完成自定义标题栏、明细栏制作,并与中望CAD绑定,完成 [中望CAD接口安装](../2.PDM/11.中望CAD接口安装.md)
|
||||
|
||||
创建出一个带标题栏与明细栏的文件(选择任意一个标题栏与明细栏即可)
|
||||
|
||||
@ -306,3 +306,13 @@ C盘 → 用户 → 公共文档 → ZWSoft → ZWCADM → 里面有中望CAD安
|
||||
最后为要提取的属性进行对应的设置,完成后点击保存即可
|
||||
|
||||

|
||||
|
||||
# 补充
|
||||
|
||||
可能会出现 [填写CAD属性,中文显示成问号](../../报错处理/填写CAD属性,中文显示成问号.md) 的问题 ,那么是因为当前字体中不包含中文字符,可以使用 `ST` 命令,在 **文字样式** 中调整文字样式使用的字体
|
||||
|
||||

|
||||
|
||||
如果是后缀为 `.shx` 的CAD专用字体,那么可以勾选 **大体字**
|
||||
|
||||

|
@ -62,3 +62,12 @@ CAXA会自动进入到标题栏的块编辑器中,在块编辑器中制作标
|
||||
|
||||

|
||||
|
||||
# 补充
|
||||
|
||||
可能会出现 [填写CAD属性,中文显示成问号](../../报错处理/填写CAD属性,中文显示成问号.md) 的问题 ,那么是因为当前字体中不包含中文字符,可以使用 `ST` 命令,在 **文字样式** 中调整文字样式使用的字体
|
||||
|
||||

|
||||
|
||||
如果是后缀为 `.shx` 的CAD专用字体,那么可以勾选 **大体字**
|
||||
|
||||

|
@ -10,7 +10,7 @@
|
||||
|
||||
## 前置要求
|
||||
|
||||
掌握CAXA绘图、属性定义等基础操作
|
||||
掌握CAXA绘图、属性定义等基础操作,使用模板需要完成 [浩辰CAD机械版接口安装](../2.PDM/15.浩辰CAD机械版接口安装.md)
|
||||
|
||||
# 制作标题栏模板
|
||||
|
||||
@ -66,3 +66,13 @@
|
||||

|
||||
|
||||
完成后将文件另存到mxb文件夹中,**名称必须与明细表头的名称相对应**
|
||||
|
||||
# 补充
|
||||
|
||||
可能会出现 [填写CAD属性,中文显示成问号](../../报错处理/填写CAD属性,中文显示成问号.md) 的问题 ,那么是因为当前字体中不包含中文字符,可以使用 `ST` 命令,在 **文字样式** 中调整文字样式使用的字体
|
||||
|
||||

|
||||
|
||||
如果是后缀为 `.shx` 的CAD专用字体,那么可以勾选 **大体字**
|
||||
|
||||

|
@ -37,3 +37,13 @@ AutoCAD的模板制作中,[块制作](28.AutoCAD图纸模板制作.md#块制
|
||||
最后为要提取的属性进行对应的设置,完成后点击保存即可
|
||||
|
||||

|
||||
|
||||
# 补充
|
||||
|
||||
可能会出现 [填写CAD属性,中文显示成问号](../../报错处理/填写CAD属性,中文显示成问号.md) 的问题 ,那么是因为当前字体中不包含中文字符,可以使用 `ST` 命令,在 **文字样式** 中调整文字样式使用的字体
|
||||
|
||||

|
||||
|
||||
如果是后缀为 `.shx` 的CAD专用字体,那么可以勾选 **大体字**
|
||||
|
||||

|
@ -314,11 +314,11 @@
|
||||
|
||||
#### 前置要求
|
||||
|
||||
要求当前用户在当前文件夹下至少具备 [变更](20.文件权限管理.md#变更) 权限,处于 发布态 的文件需要开启 [发布态文件变更](210.文件变更管理.md#发布态文件变更) 的相关参数
|
||||
要求当前用户在当前文件夹下至少具备 [变更](20.文件权限管理.md#变更) 权限,处于 发布态 的文件需要开启 [发布态文件变更](215.文档变更管理.md#发布态文件变更) 的相关参数
|
||||
|
||||
#### 变更操作
|
||||
|
||||
对于归档区的文件,若要进行正式的内容变更,则需要用户具备变更权限,并且执行变更操作,选中要变更的文件,选择 **生命周期** → **变更**,然后选择该文件变更时要走的流程即可
|
||||
对于归档区的文件,若要进行正式的内容变更,则需要用户具备变更权限,并且执行变更操作,选中要变更的文件,选择 **生命周期** → **变更**,然后选择该文件变更时要走的 [文档变更流程](104.文档变更流程.md) 即可
|
||||
|
||||

|
||||
|
||||
|
@ -6,9 +6,9 @@
|
||||
|
||||

|
||||
|
||||
截图的文件更推荐保存为位图文件,即`.bmp`文件(为了方便CAD图纸签章)
|
||||
截图的文件更推荐保存为位图文件,即 `.bmp` 文件(为了方便CAD图纸签章)
|
||||
|
||||
然后在电脑上使用画图软件打开该文件,将其像素调整为`200:100`即可
|
||||
然后在电脑上使用画图软件打开该文件,将其像素调整为 `200:100` 即可
|
||||
|
||||

|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
- **提高质量和合规性**: 标准化的流程有助于遵守行业标准和法规要求,减少潜在的合规风险,同时提高产品质量
|
||||
|
||||
有了 **文档审批流程模板** 才能 [执行文档审批流程](8.执行文档审批流程.md)
|
||||
|
||||
# 前置要求
|
||||
|
||||
制作流程模板,需要用户角色至少具备 **流程模板** 的 **可见、浏览、修改、新增** 权限
|
||||
@ -24,7 +26,7 @@
|
||||
|
||||
## 新建文件夹
|
||||
|
||||
在 **流程模板目录** 中右键,选择 新建 → 文件夹 → 普通文件夹
|
||||
在 **流程模板目录** 中右键,选择 **新建** → **文件夹** → **普通文件夹**
|
||||
|
||||

|
||||
|
||||
@ -125,6 +127,10 @@
|
||||
>
|
||||
> 
|
||||
|
||||
系统不仅可以为 **文档** 设定操作,还可以为 [窗体表单](175.窗体表单.md) 、 [文档变更记录](210.文档变更记录分类.md) 等设定操作
|
||||
|
||||

|
||||
|
||||
### 操作
|
||||
|
||||
1. 创建 **流程模板**
|
||||
@ -151,11 +157,11 @@
|
||||
|
||||

|
||||
|
||||
7. 设置[参与人](#参与人)与[角色](#参与人角色)(开始过程可不用)
|
||||
7. 设置 [参与人](#参与人) 与 [角色](#参与人角色) (开始过程可不用)
|
||||
|
||||

|
||||
|
||||
8. 在 “ 高级设定 ” 中[设定操作](#设定操作)
|
||||
8. 在 **高级设定** 中 [设定操作](#设定操作)
|
||||
|
||||

|
||||
|
||||
@ -306,19 +312,20 @@
|
||||
|
||||
#### 条件选择
|
||||
|
||||
条件选择指用户在提交当前过程时,系统可根据 [窗体表单](175.窗体表单.md) 上设置的条件,自动触发,执行相应的过程,分有两种情况:
|
||||
条件选择仅限于 [窗体表单](175.窗体表单.md) 在制作 [流程分支设置](185.流程分支设置.md) 时使用,指用户在提交当前过程时,系统可根据 [窗体表单](175.窗体表单.md) 上设置的条件,自动触发,执行相应的过程,分有两种情况:
|
||||
|
||||
1. 自动条件选择:完全按照满足条件进行选择,用户无法选择
|
||||
2. 半自动条件选择:按照满足条件进行选择,同时用户可以选择
|
||||
|
||||

|
||||
|
||||
## 邮件通知
|
||||
|
||||
在通常情况下,工作流的每个过程都会发送一封邮件给相关参与人,该邮件属于系统内部邮箱邮件,而对于希望[外部邮箱](../0.SETOUT/40.用户设置.md#Email)收到通知的用户,想要在工作流执行到相应过程后会发送一封外部邮件给相关参与人,那么就需要在流程模板中进行相应的配置
|
||||
在通常情况下,工作流的每个过程都会发送一封邮件给相关参与人,该邮件属于系统内部邮箱邮件,而对于希望你 [外部邮箱](../0.SETOUT/40.用户设置.md#Email) 收到通知的用户,想要在工作流执行到相应过程后会发送一封外部邮件给相关参与人,那么就需要在流程模板中进行相应的配置
|
||||
|
||||
### 前置要求
|
||||
|
||||
系统发送外邮通知,前提要求是为系统配置了公用的[公司外部邮箱](../4.other/50.外部邮箱配置.md#公司外部邮箱),并且收件人也配置了[外部邮箱](../0.SETOUT/40.用户设置.md#Email)
|
||||
系统发送外邮通知,前提要求是为系统配置了公用的 [公司外部邮箱](../4.other/50.外部邮箱配置.md#公司外部邮箱) ,并且收件人也配置了 [外部邮箱](../0.SETOUT/40.用户设置.md#Email)
|
||||
|
||||
### 邮件通知配置
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
# 介绍
|
||||
|
||||
文档审批流程,指代对 [拟制态](5.文件生命周期.md#拟制态) 的文件发起线上审批流程,在审批流程中可由系统将编码、版本、日期等信息写入文件中,以确保文件内容与系统中的文档属性保持一致
|
||||
|
||||
## 前置要求
|
||||
|
||||
执行文档审批流程的前置要求是完成 [文档审批流程模板制作](75.文档审批流程模板制作.md)
|
||||
|
||||
# 发起流程
|
||||
|
||||
对于编制好,需要在系统中走线上审签的文件,需要对其发起工作流
|
@ -12,9 +12,9 @@
|
||||
|
||||

|
||||
|
||||
# 创建对象分类
|
||||
# 创建文档对象分类
|
||||
|
||||
在 **企业配置** → **对象分类** → **文档** 中可以创建 **文档分类**,对 “文档” 或文档分类右键,选择 **分类** 即可新建一个 文档分类
|
||||
在 **企业配置** → **对象分类** → **文档** 中可以创建 **文档分类**,对 “文档” 或文档分类右键,选择 **分类** 即可新建一个文档分类
|
||||
|
||||

|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
## 变更流程模板
|
||||
|
||||
点击 变更流程模板 的选择框可以在 **流程模板** 功能区选择要为该文档分类绑定的变更流程,这样在该分类的归档文档需要变更时,可以对文档右键 [生命周期](5.文件生命周期.md),直接选择 [变更](5.文件生命周期.md#变更),跳过选取流程的步骤,直接调用绑定好的变更流程
|
||||
点击 **变更流程模板** 的选择框可以在 **流程模板** 功能区选择要为该文档分类绑定的变更流程,这样在该分类的归档文档需要变更时,可以对文档右键 [生命周期](5.文件生命周期.md) ,直接选择 [变更](5.文件生命周期.md#变更) ,跳过选取流程的步骤,直接调用绑定好的变更流程流程
|
||||
|
||||

|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
|
||||
## 编码器
|
||||
|
||||
配置 **文档分类** 的编码器需要先勾选 编码器,然后通过选择框去 **编码管理** 功能区选择要为该文档分类绑定的编码器,这样在创建出该分类的文档后,可以通过编码器自动生成出相应的编码
|
||||
配置 **文档分类** 的编码器需要先勾选 **编码器**,然后通过选择框去 **编码管理** 功能区选择要为该文档分类绑定的编码器,这样在创建出该分类的文档后,可以通过编码器自动生成出相应的编码
|
||||
|
||||
文档分类配置编码器操作,与 [物料分类配置编码器](../2.PDM/25.物料分类设置.md#物料编码器) 操作相同
|
||||
|
||||
@ -136,6 +136,8 @@
|
||||
|
||||

|
||||
|
||||
# 扩展
|
||||
|
||||
## 新建文档时同步属性
|
||||
|
||||
勾选 **新建文档时同步属性** 选项,能够在新建此分类的文档时,直接将文档编码(`Code`)属性写入到文档中
|
||||
@ -145,13 +147,11 @@
|
||||
说明:
|
||||
|
||||
1. 仅限于 **文档编码** 属性,不能写入自定义属性或版本等属性
|
||||
2. 名称必须用`Code`,不可用中文,必须与 **文档属性** 的属性名保持一致
|
||||
3. 不支持文本框形式,Excel表只支持 [名称](25.Excel文档模板制作.md#名称(不推荐)),Word只支持 [书签](26.Word文档模板制作.md#书签) 形式
|
||||
2. 名称必须用 `Code`,不可用中文,必须与 **文档属性** 的属性名保持一致
|
||||
3. 不支持文本框形式,Excel表只支持 [名称](25.Excel文档模板制作.md#名称(不推荐)) ,Word只支持 [书签](26.Word文档模板制作.md#书签) 形式
|
||||
4. 不支持导入,只能新建文档时写入,导入的文件就算选择了分类也不会写入
|
||||
5. 将在本地电脑打开文件,将编码写入,可能会造成卡顿
|
||||
|
||||
# 扩展
|
||||
|
||||
## 已有文件调整文档分类
|
||||
|
||||
对于系统里已经有了对应的文档分类,但该文件是从外部导入的,在系统中被默认识别为 **普通文档** 的情况下,可以对该文档右键,选择 **属性** 后打开该文档的属性界面,选择 **文档分类** 可调整该文档的分类
|
||||
|
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211142934.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211143220.png
Normal file
After Width: | Height: | Size: 152 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211143824.png
Normal file
After Width: | Height: | Size: 543 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211144220.png
Normal file
After Width: | Height: | Size: 1009 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211144948.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211145621.png
Normal file
After Width: | Height: | Size: 1.6 MiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211150556.png
Normal file
After Width: | Height: | Size: 263 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211151517.png
Normal file
After Width: | Height: | Size: 607 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211152217.png
Normal file
After Width: | Height: | Size: 753 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211152526.png
Normal file
After Width: | Height: | Size: 291 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211154120.png
Normal file
After Width: | Height: | Size: 717 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211154256.png
Normal file
After Width: | Height: | Size: 435 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211154456.png
Normal file
After Width: | Height: | Size: 1005 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211160555.png
Normal file
After Width: | Height: | Size: 170 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250211164531.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212090345.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212092839.png
Normal file
After Width: | Height: | Size: 859 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212222527.png
Normal file
After Width: | Height: | Size: 353 KiB |
BIN
SanPinPLM/相关操作/1.EDM/assets/Pasted image 20250212222546.png
Normal file
After Width: | Height: | Size: 382 KiB |
@ -6,15 +6,15 @@
|
||||
|
||||

|
||||
|
||||
在 “ 系统设置 ” 模块中选择 “ CAD接口 ” 选项,其弹窗中选择 “ 中望 ”
|
||||
在 **系统设置** 模块中选择 **CAD接口** 选项,其弹窗中选择 **中望**
|
||||
|
||||

|
||||
|
||||
点击 “ 浏览目录 ” 按钮,找到CAXA的安装目录
|
||||
点击 **浏览目录** 按钮,找到CAXA的安装目录
|
||||
|
||||

|
||||
|
||||
选择对应的软件版本,并勾选上 “ 安装64位接口 ”、“ 安装在线菜单 ”,点击 “ 安装 ” 按钮后等待片刻即可
|
||||
选择对应的软件版本,并勾选上 **安装64位接口**、**安装在线菜单**,点击 **安装** 按钮后等待片刻即可
|
||||
|
||||

|
||||
|
||||
|